/**
*
* Witchcraft Compiler Collection
*
* Author: Jonathan Brossard - endrazine@gmail.com
*
*******************************************************************************
* The MIT License (MIT)
* Copyright (c) 2016-2025 Jonathan Brossard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************
*
*/

#include <libwitch/wsh.h>
#include <libwitch/wsh_functions.h>
#include <libwitch/wsh_help.h>
#include <libwitch/sigs.h>
#include <uthash.h>
#include <utlist.h>
#include <libgen.h>
#include <lauxlib.h>
#include <sys/sendfile.h>
#include <string.h>
#include <regex.h>
#include <link.h>

#define CARRAY_META "carray_meta"
#define CSTRUCT_META "cstruct_meta"
#define STRUCT_DEF_META "struct_def_meta"

/**
* Structures for lua2c() and struct2c()
* implementing binary reification
*/

typedef enum {
	CARRAY_CHARPTR,
	CARRAY_INT,
	CARRAY_LONG,
	CARRAY_VOIDPTR
} carray_type_t;

typedef struct {
	void *data;
	size_t length;
	carray_type_t type;
} carray_t;


// Structure field descriptor
typedef struct {
	char name[64];
	char type[32];
	size_t offset;
	size_t size;
} struct_field_t;

// Structure definition
typedef struct {
	char name[128];
	size_t total_size;
	size_t alignment;
	size_t field_count;
	struct_field_t *fields;
} struct_def_t;

// Structure instance (similar to carray_t)
typedef struct {
	void *data;
	struct_def_t *definition;
	int is_owned;		// Whether we own the memory
} cstruct_t;


/**
* Rust demangling function prototypes
*/
char* rust_demangle(const char* mangled);
void rust_demangle_free(char* ptr);

/**
* Imported function prototypes
*/
int userland_load_binary(char *fname);
int mk_lib(char *name, unsigned int noinit, unsigned int strip_vernum, unsigned int no_now_flag, unsigned int use_segments);

/**
* Forward declarations
*/
int wsh_usage(char *name);
int wsh_print_version(void);
int add_symbol(char *symbol, char *libname, char *htype, char *hbind, unsigned long value, unsigned int size, unsigned long int addr);
static int carray_ptr(lua_State *L);
static void create_carray_metatable(lua_State *L);
static int carray_debug(lua_State *L);
void init_lua2c(lua_State *L);
static int print_array(lua_State *L);
static int cstruct_gc(lua_State * L);
static int cstruct_index(lua_State * L);
static int cstruct_newindex(lua_State * L);
static int cstruct_ptr(lua_State * L);
static int struct_def_gc(lua_State * L);
static int print_struct(lua_State * L);
static int load_struct_def(lua_State * L);
static int struct2c(lua_State * L);
static int ptr2struct(lua_State * L);
static int memory2c(lua_State * L);
static void create_cstruct_metatable(lua_State * L);
static void create_struct_def_metatable(lua_State * L);
void init_struct2c(lua_State * L);

// address sanitizer macro : disable a function by prepending ATTRIBUTE_NO_SANITIZE_ADDRESS to its definition
#if defined(__clang__) || defined (__GNUC__)
# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) __attribute__((disable_sanitizer_instrumentation))
#else
# define ATTRIBUTE_NO_SANITIZE_ADDRESS
#endif

#if defined(__has_feature)
#   if __has_feature(address_sanitizer) // clang
#       define __SANITIZE_ADDRESS__ 	// GCC
#   endif
#endif

#if defined(__SANITIZE_ADDRESS__)
#define HAS_ASAN 1
#endif


#ifndef __amd64__
#define REG_RIP    16
#endif

#if defined(__i386__) || defined(__amd64__)
#define REG_EFL 0
#define REG_ERR 0
#endif

#ifdef __LP64__ // Generic 64b
#define Elf_Ehdr    Elf64_Ehdr
#define Elf_Shdr    Elf64_Shdr
#define Elf_Sym     Elf64_Sym
#define Elf_Addr    Elf64_Addr
#define Elf_Sword   Elf64_Sxword
#define Elf_Section Elf64_Half
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
#define Elf_Rel     Elf64_Rel
#define Elf_Rela    Elf64_Rela
#define ELF_R_SYM   ELF64_R_SYM
#define ELF_R_TYPE  ELF64_R_TYPE
#define ELF_R_INFO  ELF64_R_INFO
#define Elf_Phdr    Elf64_Phdr
#define Elf_Xword   Elf64_Xword
#define Elf_Word    Elf64_Word
#define Elf_Off     Elf64_Off
#define ELFCLASS    ELFCLASS64
#define ELFMACHINE  EM_X86_64
#define CS_MODE     CS_MODE_64
#define RELOC_MODE  RELOC_X86_64
#define EXTRA_VDSO  "linux-vdso.so.1"
#else           // Generic 32b
#define Elf_Ehdr    Elf32_Ehdr
#define Elf_Shdr    Elf32_Shdr
#define Elf_Sym     Elf32_Sym
#define Elf_Addr    Elf32_Addr
#define Elf_Sword   Elf64_Sword
#define Elf_Section Elf32_Half
#define ELF_ST_BIND ELF32_ST_BIND
#define ELF_ST_TYPE ELF32_ST_TYPE
#define Elf_Rel     Elf32_Rel
#define Elf_Rela    Elf32_Rela
#define ELF_R_SYM   ELF32_R_SYM
#define ELF_R_TYPE  ELF32_R_TYPE
#define ELF_R_INFO  ELF32_R_INFO
#define Elf_Phdr    Elf32_Phdr
#define Elf_Xword   Elf32_Xword
#define Elf_Word    Elf32_Word
#define Elf_Off     Elf32_Off
#define ELFCLASS    ELFCLASS32
#define ELFMACHINE  EM_386
#define CS_MODE     CS_MODE_32
#define RELOC_MODE  RELOC_X86_32
#define EXTRA_VDSO  "linux-gate.so.1"
#endif

learn_t *protorecords = NULL;

/**
* Main wsh context
*/
extern wsh_t *wsh;

#ifdef __aarch64__
#define ESR_MAGIC 0x45535201

uint64_t get_esr_from_reserved(void *reserved, size_t reserved_size) {
    size_t offset = 0;
    while (offset + sizeof(struct _aarch64_ctx) <= reserved_size) {
        struct _aarch64_ctx *hdr = (struct _aarch64_ctx *)((char *)reserved + offset);
        if (hdr->magic == 0 && hdr->size == 0)
            break;  // end marker
        if (hdr->magic == ESR_MAGIC) {
            struct esr_context *esr_ctx = (struct esr_context *)hdr;
            return esr_ctx->esr;
        }
        if (hdr->size == 0)
            break; // corrupted frame
        offset += hdr->size;
    }
    return 0; // not found
}

#endif

#ifndef __GLIBC__
#include <dlfcn.h>

int backtrace(void **buffer, int size) {
    int count = 0;
    void *current_frame = __builtin_frame_address(0);
    
    if (!current_frame || current_frame < (void*)0x1000) {
        printf("Invalid initial frame pointer: %p\n", current_frame);
        return 0;
    }
    
    void **fp = (void**)current_frame;
    printf("Using frame pointer walking from %p\n", fp);
    
    while (fp && count < size) {
        // Enhanced validation
        if (fp < (void**)0x1000 || 
            fp >= (void**)0x800000000000UL ||
            ((uintptr_t)fp & 0x7) != 0) {  // Check alignment
            printf("Invalid frame pointer: %p\n", fp);
            break;
        }
        
        void *return_addr = fp[1];
        
        // Better return address validation
        if (!return_addr || 
            return_addr < (void*)0x1000 || 
            return_addr >= (void*)0x800000000000UL) {
            printf("Invalid return address: %p\n", return_addr);
            break;
        }
        
        // Validate this is actually in a mapped executable region
        Dl_info info;
        if (!dladdr(return_addr, &info) || !info.dli_fbase) {
            printf("Return address %p not in any mapped library\n", return_addr);
            break;
        }
        
        buffer[count++] = return_addr;
        printf("Frame %d: %p (%s)\n", count-1, return_addr, 
               info.dli_fname ? info.dli_fname : "unknown");
        
        void **next_fp = (void**)fp[0];
        if (!next_fp || next_fp <= fp || next_fp >= (void**)0x800000000000UL) {
            printf("End of stack or invalid next frame: %p\n", next_fp);
            break;
        }
        fp = next_fp;
    }
    
    printf("Collected %d valid frames\n", count);
    return count;
}

char **backtrace_symbols(void *const *buffer, int size) {
    char **result = calloc(size, sizeof(char*));
    if (!result) return NULL;
    
    for (int i = 0; i < size; i++) {
        Dl_info info;
        result[i] = malloc(512);  // Increased buffer size
        if (!result[i]) continue;
        
        if (dladdr(buffer[i], &info)) {
            if (info.dli_sname) {
                snprintf(result[i], 512, "%s(%s+0x%lx) [%p]",
                    info.dli_fname ? info.dli_fname : "?",
                    info.dli_sname,
                    (char*)buffer[i] - (char*)info.dli_saddr,
                    buffer[i]);
            } else {
                snprintf(result[i], 512, "%s(+0x%lx) [%p]",
                    info.dli_fname ? info.dli_fname : "?",
                    (char*)buffer[i] - (char*)info.dli_fbase,
                    buffer[i]);
            }
        } else {
            snprintf(result[i], 512, "[%p]", buffer[i]);
        }
    }
    return result;
}
#endif

/**
* Bruteforce valid memory mapping ranges
*/
int bfmap(lua_State * L)
{
	unsigned long int pcounter = 0;
	unsigned int page_size = 4096;

	unsigned long long int i = 0, j = 0, r = 0;

	printf(GREEN "\n   Memory segments\n\n");

	for (r = 0; r < sizeof(ranges) / sizeof(range_t); r++) {
		for (i = ranges[r].min; i < ranges[r].max; i += page_size) {

			if (pcounter++ == 10000) {
				pcounter = 0;
				printf("  %016llx\r", i);
			}

			if (msync((void *) i, page_size, MS_ASYNC)) {
				continue;
			}	// Invalid page

			// We found a valid page, find length of mapping
			for (j = 0; j < 0x100000000; j += page_size) {
				if (msync((void *) i + j, page_size, 0)) {
					break;
				}
			}
			printf(NORMAL "  %016llx-%016llx\n" GREEN, i, i + j);
			i += j;
		}
	}
	printf(NORMAL "                       \r\n");
	return 0;
}

/**
* Get permissions in human readable format
*/
int ptoh(int perms, char hperms[])
{
	snprintf(hperms, 5, "%s%s%s", (perms & 0x04) ? "r" : "-", (perms & 0x02) ? "w" : "-", (perms & 0x01) ? "x" : "-");
	return 0;
}

/**
* Print information on a given function
*/
void info_function(void *addr)
{
	Dl_info dli;
	dladdr(addr, &dli);
	printf(" -- %s() = %p	from %s:%p\n", dli.dli_sname, dli.dli_saddr, dli.dli_fname, dli.dli_fbase);
}

/**
* Fatal error : print an error message and exit with error
*/
void fatal_error(lua_State * L, char *msg)
{
	fprintf(stderr, "\nFATAL ERROR:\n  %s: %s\n\n", msg, lua_tostring(L, -1));
	_Exit(EXIT_FAILURE);
}

/**
* Simple hexdump routine
*/
void hexdump(uint8_t * data, size_t size, size_t colorstart, size_t color_len)
{
	size_t i = 0, j = 0;

	for (j = 0; j < size; j += 16) {

		// Highlight offset in greed
		if (wsh->opt_hollywood) {
			printf(GREEN);
		}
		printf("%p    ", data + j);

		if (wsh->opt_hollywood) {
			printf(NORMAL);
		}

		for (i = j; i < j + 16; i++) {

			// Highlight match in red
			if ((wsh->opt_hollywood) && (color_len) && (colorstart == i)) {
				printf(RED);
			}
			if ((wsh->opt_hollywood) && (color_len) && (colorstart + color_len == i)) {
				printf(NORMAL);
			}

			if (i < size) {
				printf("%02x ", data[i] & 255);
			} else {
				printf("   ");
			}
		}

		printf("   ");

		for (i = j; i < j + 16; i++) {

			// Highlight match in red
			if ((wsh->opt_hollywood) && (color_len) && (colorstart == i)) {
				printf(RED);
			}
			if ((wsh->opt_hollywood) && (color_len) && (colorstart + color_len == i)) {
				printf(NORMAL);
			}

			if (i < size)
				putchar(32 <= (data[i] & 127) && (data[i] & 127) < 127 ? data[i] & 127 : '.');
			else
				putchar(' ');
		}
		putchar('\n');
	}
}

/**
* Resolve the address of a symbol within a given library
*/
static unsigned long int resolve_addr(char *symbol, char *libname)
{
	unsigned long int ret = 0;
	struct link_map *handle = 0;
	Dl_info dli;

	if ((!symbol) || (!*symbol)) {
		return -1;
	}

	handle = dlopen(libname, wsh->opt_global ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW);
	if (!handle) {
		fprintf(stderr, "ERROR: %s\n", dlerror());
		_Exit(EXIT_FAILURE);
	}

	dlerror();		/* Clear any existing error */

	ret = (unsigned long int) dlsym(handle, symbol);

	if (!ret) {
#ifdef PEDANTIC_WARNINGS
		char *err = 0;
		err = dlerror();

		if (err) {
			fprintf(stderr, "ERROR: %s\n", err);
			//_Exit(EXIT_FAILURE);
		}
#endif
		/* Ignore the symbol */
		dlclose(handle);

		return -1;
	}

	dladdr((void *) ret, &dli);

	// Is it the correct lib ?
	if ((dli.dli_fname) && (libname) && (strlen(libname)) && (strncmp(libname, dli.dli_fname, strlen(libname)))) {
		ret = -1;
	}

	dlclose(handle);

	return ret;
}

/**
* Return symbol binding type in human readable format
*/
char *symbol_tobind(int n)
{
	switch (n) {
	case 0:
		return "Local";
	case 1:
		return "Global";
	case 2:
		return "Weak";
	case 10:
		return "Unique";
	case 11:
		return "Secondary";
	default:
		break;
	}

	return "Default";
}

/**
* Return symbol type in human readable format
*/
char *symbol_totype(int n)
{
	switch (n) {
	case 0:
		return "Notype";
	case 1:
		return "Object";
	case 2:
	case STT_GNU_IFUNC:
		return "Function";
	case 3:
		return "SECTION";
	case 4:
		return "File";
	case 5:
		return "Common";
	case 6:
		return "TLS";
	default:
		break;
	}

	return "Default";
}

unsigned int ltrace(void)
{
	return 0;
}


/**
* Scan a symbol, save it to linked list
*/
int scan_symbol(char *symbol, char *libname)
{
	struct link_map *handle;
	Dl_info dli;
	Elf_Sym *s = 0;
	char *htype = 0, *hbind = 0;
	unsigned long int ret = 0;
	unsigned int stype = 0, sbind = 0;
	int retv = 0;

	handle = dlopen(libname, wsh->opt_global ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW);
	if (!handle) {
		fprintf(stderr, "ERROR: %s\n", dlerror());
		_Exit(EXIT_FAILURE);
	}

	dlerror();		/* Clear any existing error */

	ret = (unsigned long int) dlsym(handle, symbol);

#ifdef __GLIBC__
	if ((dladdr1((void*)ret, &dli, (void **) &s, RTLD_DL_SYMENT))&&(s)) {

		stype = ELF_ST_TYPE(s->st_info);
		htype = symbol_totype(stype);

		sbind = ELF_ST_BIND(s->st_info);
		hbind = symbol_tobind(sbind);

		retv = add_symbol(symbol, libname, htype, hbind, s->st_value, s->st_size, ret);
		if(retv){return retv;}
#else
	if (dladdr((void*)ret, &dli)) {
		stype = 2; // Assume STT_FUNC
		htype = symbol_totype(stype);
		sbind = 1; // Assume global
		hbind = symbol_tobind(sbind);

		retv = add_symbol(symbol, libname, htype, hbind, (unsigned long)ret - (unsigned long)dli.dli_fbase, 0, ret); // size=0
#endif
	}

	dlclose(handle);
	return 0;
}

/**
* Shell autocompletion routine
*/
void completion(const char *buf, linenoiseCompletions * lc)
{
	/**
	* We want to add the next word uppon 'tab' completion,
	* exposing all the internally available keywords dynamically
	*/
	char *opt, *word = 0;
	unsigned int n = 0, i = 0;
	unsigned int p = 0, w = 0;

	n = strlen(buf);
	switch (n) {
	case 0:
		// No letter given, add default commands to completion options
		for (i = 0; i < sizeof(default_options) / sizeof(char *); i++) {
			linenoiseAddCompletion(lc, default_options[i]);
		}

		// Add reflected symbols
		symbols_t *s, *stmp;
		DL_FOREACH_SAFE(wsh->symbols, s, stmp) {
			linenoiseAddCompletion(lc, s->symbol);
		}

		return;
		break;
	default:// Input buffer is non empty:
		// the n first characters need to stay,
		// the last word needs to be completed with possible options
		opt = strdup(buf);
		// find position of last word
		for (p = strlen(opt); p > 0; p--) {
			if ((opt[p] == 0x20)||(opt[p] == 0x28)) {
				w = p + 1;
				break;
			}
		}
		// last word now starts at opt[w]
		word = opt + w;

		if (strlen(word) == n) {	// There is no space in input tokens : it is a single command, add all those that match

			// Add core functions
			for (i = 0; i < sizeof(default_options) / sizeof(char *); i++) {
				if (!strncmp(buf, default_options[i], strlen(buf))) {
					linenoiseAddCompletion(lc, default_options[i]);
				}
			}

			// Add all lua default functions
			for (i = 0; i < sizeof(lua_default_functions) / sizeof(char *); i++) {
				if (!strncmp(buf, lua_default_functions[i], strlen(buf))) {
					linenoiseAddCompletion(lc, lua_default_functions[i]);
				}
			}

			// Add reflected symbols
			symbols_t *s, *stmp;
			DL_FOREACH_SAFE(wsh->symbols, s, stmp) {
				if (!strncmp(buf, s->symbol, strlen(buf))) {
					linenoiseAddCompletion(lc, s->symbol);
				}
			}


		} else {	// There is more than one word in this command
//TODO

		}
//              linenoiseAddCompletion(lc, buf);
		break;
	}

}

/**
* Disable ASLR
*/
int disable_aslr(void)
{
	int fd = 0;
	char c = 0x30;

	fd = open(PROC_ASLR_PATH, O_RDWR);
	if (fd < 0) {
		fprintf(stderr, "ERROR : open(%s, O_RDWR) %s\n", PROC_ASLR_PATH, strerror(errno));
		return -1;
	}
	write(fd, &c, 1);
	close(fd);
	return 0;
}

/**
* Enable ASLR
*/
int enable_aslr(void)
{
	int fd = 0;
	char c = 0x32;

	fd = open(PROC_ASLR_PATH, O_RDWR);
	if (fd < 0) {
		fprintf(stderr, "ERROR : open(%s,O_RDWR) %s\n", PROC_ASLR_PATH, strerror(errno));
		return -1;
	}
	write(fd, &c, 1);
	close(fd);
	return 0;
}

/**
* Display detailed help
*/
int detailed_help(char *name)
{
	unsigned int i = 0;

	/**
	* Search command
	*/
	for(i=0 ; i < sizeof(cmdhelp)/sizeof(help_t) ; i++){
		if(!strncmp(cmdhelp[i].name, name, strlen(cmdhelp[i].name))){
			printf("\n\tWSH HELP FOR COMMAND %s\n\n\n", name);
			printf("NAME\n\n\t%s\n\nSYNOPSIS\n\n\t%s %s\n\nDESCRIPTION\n\n\t%s\n\nRETURN VALUES\n\n\t%s\n\n\n", cmdhelp[i].name, cmdhelp[i].name, cmdhelp[i].proto, cmdhelp[i].descr, cmdhelp[i].retval);
			return 0;
		}
	}

	/**
	* Search function
	*/
	for(i=0 ; i < sizeof(fcnhelp)/sizeof(help_t) ; i++){
		if(!strncmp(fcnhelp[i].name, name, strlen(fcnhelp[i].name))){
			printf("\n\tWSH HELP FOR FUNCTION %s\n\n\n", name);
			printf("NAME\n\n\t%s\n\nSYNOPSIS\n\n\t%s%s(%s)\n\nDESCRIPTION\n\n\t%s\n\nRETURN VALUES\n\n\t%s\n\n\n", fcnhelp[i].name, fcnhelp[i].protoprefix, fcnhelp[i].name, fcnhelp[i].proto, fcnhelp[i].descr, fcnhelp[i].retval);
			return 0;
		}
	}

	printf("ERROR:\tNo help available for function %s()\n", name);
	return 0;
}

/**
* Display help
*/
int help(lua_State * L)
{
	const char *arg = 0;

	if (lua_isstring(L, 1)) {
		arg = luaL_checkstring(L, 1);
		detailed_help((char *) arg);
	} else {
		printf("  [Shell commands]\n\n\thelp, quit, exit, shell, exec, clear\n\n");
		printf("  [Functions]\n\n");
		printf(" + basic:\n\thelp(), man()\n\n");
		printf(" + memory display:\n\t hexdump(), hex_dump(), hex()\n\n");
		printf(" + memory maps:\n\tshdrs(), phdrs(), map(), procmap(), bfmap()\n\n");
		printf(" + symbols:\n\tsymbols(), functions(), objects(), info(), search(), headers()\n\n");
		printf(" + memory search:\n\tgrep(), grepptr()\n\n");
		printf(" + load libaries:\n\tloadbin(), libs(), entrypoints(), rescan()\n\n");
		printf(" + code execution:\n\tlibcall()\n\n");
		printf(" + buffer manipulation:\n\txalloc(), ralloc(), xfree(), balloc(), bset(), bget(), rdstr(), rdnum()\n\n");
		printf(" + control flow:\n\t breakpoint(), bp()\n\n");
		printf(" + system settings:\n\tenableaslr(), disableaslr()\n\n");
		printf(" + settings:\n\t verbose(), hollywood()\n\n");
		printf(" + disassembly: disasm(), disasm_sym()\n\n");
		printf(" + architecture management: arch_set(), arch_info(), arch_list()\n\n");
		printf(" + structure manipulation: lua2c(), struct2c(), memory2c(), load_struct_def(), ptr2struct()\n\n");
		printf(" + advanced:\n\tltrace()\n\nTry help(\"cmdname\") for detailed usage on command cmdname.\n\n");
	}
	return 0;
}

/**
* Decode Segment flags
*/
char *decode_flags(unsigned int flags)
{
	char message[20];
	unsigned int pf_x = (flags & 0x1);
	unsigned int pf_w = (flags & 0x2);
	unsigned int pf_r = (flags & 0x4);

	memset(message, 0x00, 20);
	if (pf_r){
		strcat(message, "r");
	}else{
		strcat(message, "-");
	}
	if (pf_w){
		strcat(message, "w");
	}else{
		strcat(message, "-");
	}
	if (pf_x){
		strcat(message, "x");
	}else{
		strcat(message, "-");
	}
	return strdup(message);
}

/**
* Decode Segment type
*/
char *decode_type(unsigned int type)
{
	char *ret = 0;

	switch (type) {
	case 0:
		return "PT_NULL";
		break;
	case 1:
		return "PT_LOAD";
		break;
	case 2:
		return "PT_DYNAMIC";
		break;
	case 3:
		return "PT_INTERP";
		break;
	case 4:
		return "PT_NOTE";
		break;
	case 5:
		return "PT_SHLIB";
		break;
	case 6:
		return "PT_PHDR";
		break;
	case 7:
		return "PT_TLS";
		break;
	case 8:
		return "PT_NUM";
		break;
	case 0x6474e550:
		return "PT_GNU_EH_FRAME";
		break;
	case 0x6474e551:
		return "PT_GNU_STACK";
		break;
	case 0x6474e552:
		return "PT_GNU_RELRO";
		break;

	case 0x6474e553:
		return "PT_GNU_PROPERTY";
		break;

	default:
		ret = calloc(1, 200);
		snprintf(ret, 199, "Unknown: 0x%x\n", type);
		return ret;	// leak
		break;
	}
}

/**
* Callback function to parse Program headers (ELF Segments)
*/
int phdr_callback(struct dl_phdr_info *info, size_t size, void *data)
{
	char *pflags = 0, *ptype = 0;
	const char *fname = 0;
	Elf_Phdr *p = 0;
	int j = 0;

	if (wsh->opt_verbose) {
		printf("DEBUG: phdr_callback - name='%s', addr=%p, phnum=%d\n", 
			info->dlpi_name ? info->dlpi_name : "(null)", 
			(void*)info->dlpi_addr, info->dlpi_phnum);
	}

	for (j = 0; j < info->dlpi_phnum; j++) {
		p = (Elf_Phdr *) &info->dlpi_phdr[j];

		pflags = p ? decode_flags(p->p_flags) : 0;
		ptype = decode_type(p->p_type);
		fname = info->dlpi_name;
		if((!fname)||(strlen(fname) < 2)){
			// On musl, the main program has dlpi_name="" or "/proc/self/exe"
			// Don't skip it - use a reasonable name instead
			if(info->dlpi_addr != 0) {  // This is likely the main program
				fname = wsh->selflib ? wsh->selflib : "[main]";
		    	} else {
				if(info->dlpi_addr + p->p_vaddr >= 0x7fd000000000){
					fname = "[vdso]";
				} else {
					fname = "[unknown]";
				}
		    	}
		}

		// Save segment
		segment_add(info->dlpi_addr + p->p_vaddr, p->p_memsz, pflags, fname, ptype, p->p_flags);
	}

	return 0;
}

/**
* Add a symbol to linked list
*/
int add_symbol(char *symbol, char *libname, char *htype, char *hbind, unsigned long value, unsigned int size, unsigned long int addr)
{
	symbols_t *s = 0;
	symbols_t *si = 0, *stmp = 0, *res = 0;

	s = calloc(1, sizeof(symbols_t));
	if(!s){ fprintf(stderr, "Error: calloc() = %s\n", strerror(errno)); return -1; }
	s->addr = addr;
	s->symbol = strdup(symbol);
	s->size = size;
	s->value = value;
	s->libname = strdup(libname);
	s->htype = strdup(htype);
	s->hbind = strdup(hbind);

	// search this element in linked list
	DL_FOREACH_SAFE(wsh->symbols, si, stmp) {
		// same symbol name
		if((!strncmp(si->symbol,s->symbol,strlen(si->symbol)))&&(strlen(si->symbol) == strlen(s->symbol))){
			res = si;
		}
	}
	if(res){ return 1; } // already in linked list

	DL_APPEND(wsh->symbols, s);
	return 0;
}


/**
* Add a section to linked list
*/
void section_add(unsigned long int addr, unsigned long int size, char *libname, char *name, char *perms, int flags)
{
	sections_t *s = 0;

	s = calloc(1, sizeof(sections_t));
	if(!s){ fprintf(stderr, "Error: calloc() = %s\n", strerror(errno)); return; }
	s->addr = addr;
	s->size = size;
	s->flags = flags;
	s->libname = strdup(libname);
	s->name = strdup(name);
	s->perms = strdup(perms);

	DL_APPEND(wsh->shdrs, s);
}

/**
* Add a segment to linked list
*/
void segment_add(unsigned long int addr, unsigned long int size, char *perms, char *fname, char *ptype, int flags)
{
	segments_t *s = 0;

	s = calloc(1, sizeof(segments_t));
	if(!s){ fprintf(stderr, "Error: calloc() = %s\n", strerror(errno)); return; }
	s->addr = addr;
	s->size = size;
	s->flags = flags;
	s->libname = strdup(fname);
	s->perms = strdup(perms);
	s->type = strdup(ptype);

	DL_APPEND(wsh->phdrs, s);
}

/**
* Add an entry point to linked list
*/
void entry_point_add(unsigned long long int addr, char *fname)
{
	eps_t *s = 0;

	s = calloc(1, sizeof(eps_t));
	s->name = strdup(fname);
	s->addr = addr;

	DL_APPEND(wsh->eps, s);
}

/**
* Parse a section from an ELF
*/
void scan_section(Elf_Shdr * shdr, char *strTab, int shnum, char *fname, unsigned long int baseaddr)
{
	int i = 0;
	char hperms[5];

	for (i = 0; i < shnum; i++) {
		memset(hperms, 0x00, 5);
		snprintf(hperms, 5, "%s%s%s", (shdr[i].sh_flags & 0x02) ? "r" : "-", (shdr[i].sh_flags & 0x01) ? "w" : "-", (shdr[i].sh_flags & 0x04) ? "x" : "-");

		if(shdr[i].sh_addr){
			section_add(shdr[i].sh_addr + baseaddr, shdr[i].sh_size, fname, &strTab[shdr[i].sh_name], hperms, shdr[i].sh_flags);
		}
	}
}

/**
* Parse all sections from an ELF
*/
int scan_sections(char *fname, unsigned long int baseaddr)
{
	void *data = 0;
	Elf_Ehdr *elf = 0;
	Elf_Shdr *shdr = 0;
	int fd = 0;
	char *strtab = 0;

	fd = open(fname, O_RDONLY);
	data = mmap(NULL, lseek(fd, 0, SEEK_END), PROT_READ, MAP_SHARED, fd, 0);
	elf = (Elf_Ehdr *) data;
	if(elf == (void*)-1){ return -1; }

	entry_point_add(elf->e_entry + baseaddr, fname);

	shdr = (Elf_Shdr *) (data + elf->e_shoff);
	strtab = (char *) (data + shdr[elf->e_shstrndx].sh_offset);
	scan_section(shdr, strtab, elf->e_shnum, fname, baseaddr);
	close(fd);
	return 0;
}

/**
* Callback function to parse Section headers (ELF Sections)
*/
int shdr_callback(struct dl_phdr_info *info, size_t size, void *data)
{
	const char *libname = info->dlpi_name;
	if((!libname) || (strlen(libname) < 2)){
		// On musl, main program might have empty or short name
		// Use selflib or a default name instead of skipping
		libname = wsh->selflib ? wsh->selflib : "/proc/self/exe";
	}
	scan_sections(libname, info->dlpi_addr);

	return 0;
}

/**
* Display Program headers (ELF Segments)
*/
int phdrs(lua_State * L)
{
	print_phdrs();
	return 0;
}

/**
* Find section from address
*/
sections_t *section_from_addr(unsigned long int addr)
{
	sections_t *s = 0, *stmp = 0, *res = 0;

	DL_FOREACH_SAFE(wsh->shdrs, s, stmp) {
		if((s->addr <= addr)&&(s->addr + s->size >= addr)){
			res = s;
		}
	}
	return res;
}

/**
* Find segment from address
*/
segments_t *segment_from_addr(unsigned long int addr)
{
	segments_t *s = 0, *stmp = 0, *res = 0;

	DL_FOREACH_SAFE(wsh->phdrs, s, stmp) {
		if((s->addr <= addr)&&(s->addr + s->size >= addr)){
			res = s;
		}
	}
	return res;
}

/**
* Return a symbol from an address
*/
symbols_t *symbol_from_addr(unsigned long int addr)
{
	symbols_t *s = 0, *stmp = 0, *res = 0;

	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {
		if((s->addr <= addr)&&(s->addr + s->size >= addr)){
			res = s;
		}
	}
	return res;
}

/**
* Return a symbol from its name
*/
symbols_t *symbol_from_name(char *fname)
{
	symbols_t *s = 0, *stmp = 0;

	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {
		if(!strncmp(fname,s->symbol,strlen(fname))){
			return s;
		}
	}
	return NULL;
}


/**
* Generate headers
*/
int headers(lua_State * L)
{
	symbols_t *s = 0, *stmp = 0;
	unsigned int scount = 0;

	char *libname = 0;

	read_arg1(libname);

	printf("/**\n*\n* Automatically generated by the Witchcraft Compiler Collection %s\n*\n* %s %s\n*\n*/\n\n\n", WVERSION, WTIME, WDATE);

	/**
	* generate headers for imported objects
	*/
	printf("/**\n* Imported objects\n**/\n");
	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {

		if((!libname)||(strstr(s->libname, libname))){
			if(!strncmp(s->htype,"Object",6)){
				scount++;
				printf("extern void *%s;\n", s->symbol);
			}
		}
	}

	/**
	* generate forward prototypes for imported functions
	*/
	printf("\n\n/**\n* Imported functions\n**/\n");
	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {

		if((!libname)||(strstr(s->libname, libname))){
			if(strncmp(s->htype,"Object",6)){
				if(strncmp(s->symbol, "main", 5)){
					printf("void *%s();\n", s->symbol);
				}
			}
		}
	}

	return 0;
}


/**
* Empty linked list of symbols
*/
int empty_symbols(void)
{
	symbols_t *s = 0, *stmp = 0;

	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {
			DL_DELETE(wsh->symbols, s);
			free(s->symbol);
			free(s->hbind);
			free(s->libname);
			free(s->htype);
			free(s);

	}

	return 0;
}

/**
* Empty linked list of segments
*/
int empty_phdrs(void)
{
	segments_t *s = 0, *stmp = 0;

	DL_FOREACH_SAFE(wsh->phdrs, s, stmp) {
			DL_DELETE(wsh->phdrs, s);

			free(s->type);
			free(s->libname);
			free(s->perms);
			free(s);

	}

	return 0;
}

/**
* Empty linked list of sections
*/
int empty_shdrs(void)
{
	sections_t *s = 0, *stmp = 0;

	DL_FOREACH_SAFE(wsh->shdrs, s, stmp) {
			DL_DELETE(wsh->shdrs, s);

			free(s->name);
			free(s->libname);
			free(s->perms);
			free(s);
	}

	return 0;
}

/**
* Empty linked list of entry points
*/
int empty_eps(void)
{
	eps_t *s = 0, *stmp = 0;

	DL_FOREACH_SAFE(wsh->eps, s, stmp) {
			DL_DELETE(wsh->eps, s);

			free(s->name);
			free(s);
	}

	return 0;
}

/**
* Display program headers (ELF Segments)
*/
int print_phdrs(void)
{
	char *lastlib = "";
	segments_t *s = 0, *stmp = 0;
	unsigned int scount = 0;
	DL_COUNT(wsh->phdrs, s, scount);

	printf(" -- Total: %u segments\n", scount);	

	DL_FOREACH_SAFE(wsh->phdrs, s, stmp) {
			if(strncmp(lastlib,s->libname,strlen(lastlib))){
				printf("\n");
			}
			lastlib = s->libname;
			char *pcolor = DARKGRAY;	// NORMAL

			switch (s->flags) {
			case 4:	// r--
				pcolor = GREEN;
				break;
			case 6:	// rw-
				pcolor = BLUE;
				break;
			case 5:	// r-x
				pcolor = RED;
				break;
			case 7:	// rwx
				pcolor = MAGENTA;
				break;
			default:
				break;
			}

			if(s->size == 0){
				pcolor = DARKGRAY;
			}


			if(wsh->opt_hollywood){
				printf(NORMAL "%012lx-%012lx%s\t%s\t%lu\t%s\t%s"NORMAL"\n", s->addr, s->addr + s->size,
					pcolor, s->perms, s->size, s->libname, s->type);
			}else{
				printf("%012lx-%012lx\t%s\t%lu\t%s\t%s\n", s->addr, s->addr + s->size,
					s->perms, s->size, s->libname, s->type);
			}
	}

	printf("\n");
	printf(" -- Total: %u segments\n", scount);	

	return 0;
}

const char* indirect_function_names[] = {
	"__gettimeofday",
	"__memcmpeq",
	"__memcpy_chk",
	"__memmove_chk",
	"__mempcpy",
	"__mempcpy_chk",
	"__memset_chk",
	"__rawmemchr",
	"__stpcpy",
	"__stpncpy",
	"__strcasecmp",
	"__strcasecmp_l",
	"__strncasecmp_l",
	"__wmemset_chk",
	"bcmp",
	"gettimeofday",
	"index",
	"memchr",
	"memcmp",
	"memcpy",
	"memmove",
	"mempcpy",
	"memrchr",
	"memset",
	"rawmemchr",
	"rindex",
	"stpcpy",
	"stpncpy",
	"strcasecmp",
	"strcasecmp_l",
	"strcat",
	"strchr",
	"strchrnul",
	"strcmp",
	"strcpy",
	"strcspn",
	"strlen",
	"strncasecmp",
	"strncasecmp_l",
	"strncat",
	"strncmp",
	"strncpy",
	"strnlen",
	"strpbrk",
	"strrchr",
	"strspn",
	"strstr",
	"time",
	"wcschr",
	"wcscmp",
	"wcscpy",
	"wcslen",
	"wcsncmp",
	"wcsnlen",
	"wcsrchr",
	"wmemchr",
	"wmemcmp",
	"wmemset",
	NULL
};

/**
* Helper function
*/
/*
uintptr_t find_libc_base() {
    uintptr_t libc_pointer;
    for(libc_pointer = (uintptr_t)printf & (~0xFFF); *(uint32_t*)libc_pointer != 0x464c457f; libc_pointer-=0x1000);
    return libc_pointer;
}
*/
#define MAX_NUM_IMPL 256

//From "include/ifunc-impl-list.h"
struct libc_ifunc_impl
{
  /* The name of function to be tested.  */
  const char *name;
  /* The address of function to be tested.  */
  void (*fn) (void);
  /* True if this implementation is usable on this machine.  */
  bool usable;
};

extern size_t __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, size_t max);


#ifdef __GLIBC__
/**
* Load Indirect functions
*/
int load_indirect_functions(lua_State * L)
{
    struct libc_ifunc_impl entries[MAX_NUM_IMPL];
	size_t i = 0;
	int retv = 0;

    for(const char** function_name = indirect_function_names; *function_name; function_name++) {
        size_t num_entries = __libc_ifunc_impl_list(*function_name, entries, MAX_NUM_IMPL);

	void *addr =  dlsym(NULL, *function_name);

	Dl_info info;
	Elf64_Sym *mydlsym = calloc(1, sizeof(Elf64_Sym));
#ifdef __GLIBC__
	if (dladdr1(addr, &info, (void **) &mydlsym, RTLD_DL_SYMENT)) {
		retv = add_symbol(*function_name, info.dli_fname, "Function", "Global", dlsym(NULL, *function_name)-info.dli_fbase, 8, dlsym(NULL, *function_name));
#else
	if (dladdr(addr, &info)) {
		retv = add_symbol(entries[i].name, info.dli_fname, "Function", "Global", (void*)((uintptr_t)entries[i].fn)-info.dli_fbase, 0, (void*)((uintptr_t)entries[i].fn));
#endif
	}else{
		retv = add_symbol(*function_name, "indirect:libc.so", "Function", "Global", dlsym(NULL, *function_name), 8, dlsym(NULL, *function_name));
	}

	char *luacmd = calloc(1, 2048);
	snprintf(luacmd, 2047, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%p, a, b, c, d, e, f, g, h); return j, k; end\n", *function_name, dlsym(NULL, *function_name));
	luabuff_append(luacmd);
	exec_luabuff();
	free(luacmd);

        for(i = 0; i < num_entries; i++) {
#ifdef __GLIBC__
		if (dladdr1((void*)((uintptr_t)entries[i].fn), &info, (void **) &mydlsym, RTLD_DL_SYMENT)) {
			retv = add_symbol(entries[i].name, info.dli_fname, "Function", "Global", (void*)((uintptr_t)entries[i].fn)-info.dli_fbase, 8, (void*)((uintptr_t)entries[i].fn));
		}else{
			retv = add_symbol(entries[i].name, "indirect:libc.so", "Function", "Global", (void*)((uintptr_t)entries[i].fn), 8, (void*)((uintptr_t)entries[i].fn));
		}
#else
		if (dladdr((void*)((uintptr_t)entries[i].fn), &info)) {
			retv = add_symbol(entries[i].name, info.dli_fname, "Function", "Global", (void*)((uintptr_t)entries[i].fn)-info.dli_fbase, 0, (void*)((uintptr_t)entries[i].fn));
		}else{
			retv = add_symbol(entries[i].name, "indirect:libc.so", "Function", "Global", (void*)((uintptr_t)entries[i].fn), 0, (void*)((uintptr_t)entries[i].fn));
		}
#endif

		char *luacmd = calloc(1, 2048);
		snprintf(luacmd, 2047, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%p, a, b, c, d, e, f, g, h); return j, k; end\n", entries[i].name, (void*)((uintptr_t)entries[i].fn));
		luabuff_append(luacmd);
		exec_luabuff();
		free(luacmd);
        }
    }

    return 0;

	return 0;
}

/**
* Display Indirect functions
*/
int print_indirect_functions(lua_State * L)
{
    struct libc_ifunc_impl entries[MAX_NUM_IMPL];

    for(const char** function_name = indirect_function_names; *function_name; function_name++) {
        size_t num_entries = __libc_ifunc_impl_list(*function_name, entries, MAX_NUM_IMPL);

	void *addr =  dlsym(NULL, *function_name);

	Dl_info info;
	if (dladdr(addr, &info)) {
		printf("%p %s %s %p %lx\n", addr, *function_name,info.dli_fname, info.dli_fbase, addr - info.dli_fbase);
	}
        for(size_t i = 0; i < num_entries; i++) {
		addr =  dlsym(NULL, *function_name);
		if (dladdr(addr, &info)) {
			printf("  %p %s %s %p %lx\n", addr, entries[i].name,info.dli_fname, info.dli_fbase, addr - info.dli_fbase);
		}
        }
    }

    return 0;
}

#else
int load_indirect_functions(lua_State * L) {
  if (wsh->opt_verbose) {
    printf("WARNING: Indirect functions not supported on this libc\n");
  }
  return 0;
}

int print_indirect_functions(lua_State * L) {
  return 0;
}
#endif


/**
* Display symbols
*/
int print_symbols(lua_State * L)
{
	unsigned int scount = 0;
	symbols_t *s = 0, *stmp = 0;
	unsigned int i = 0;
	unsigned int pcnt = 0;
	char *symname = 0;
	char *libname = 0;
	unsigned int returnall = 0;

	read_arg1(symname);
	read_arg2(libname);
	read_arg3(returnall);

	DL_COUNT(wsh->symbols, s, scount);

	if(returnall < 2){
		printf(" -- Total: %u symbols\n", scount);	
		printf(" -- Symbols:\n\n");
	//      printf("    Type       Size                     Path                  Address              Name           (Demangled)\n");
		printf("-----------------------------------------------------------------------------------------------------------------\n");
	}

	/* create result table */
	lua_newtable(L);

	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {
		if((!symname)||(strstr(s->symbol, symname))){
			if((!libname)||(strstr(s->libname, libname))){

				if(returnall < 2){
					printf("%s ", s->libname);
					for (i = strlen(s->libname); i < 40; i++)
						printf(" ");
					printf("%s ", s->symbol);
					for (i = strlen(s->symbol); i < 30; i++)
						printf(" ");
					printf("%s ", s->htype);
					for (i = strlen(s->htype); i < 10; i++)
						printf(" ");
					printf(" %s	%lx	\t\t%lu	%lx\n", s->hbind, s->value, s->size, s->addr);
				}

				/* Add symbol to Lua table */
				lua_pushstring(L, s->symbol);		/* push key */
				lua_getglobal(L, s->symbol);		/* get pointer to global with this name : keep it as value on top of stack */
			        lua_settable(L, -3);

				pcnt++;
				if((!returnall)&&(pcnt == LINES_MAX)){ pcnt = 0; int c = getchar(); switch(c){case 0x61: pcnt = LINES_MAX + 1; break; case 0x71: return 0; break; default: break;   }; }
			}
		}
	}

	if(returnall < 2){
		printf("\n");
		printf(" -- %u symbols matched\n", pcnt);	
	}

	// Return scount as second return value
	lua_pushinteger(L, scount);

	return 2;	// Return 1 table + number of match
}

/**
* Display functions
*/
int print_functions(lua_State * L)
{
	unsigned int scount = 0;
	symbols_t *s = 0, *stmp = 0;
	unsigned int i = 0;
	unsigned int pcnt = 0;
	char *libname = 0;
	char *symname = 0;
	unsigned int returnall = 0;

	read_arg1(symname);
	read_arg2(libname);
	read_arg3(returnall);

	DL_COUNT(wsh->symbols, s, scount);

	if(returnall < 2){
		printf(" -- Total: %u symbols\n", scount);	
		printf(" -- Functions:\n");
		printf("-----------------------------------------------------------------------------------------------------------------\n");
	}

	scount = 0;

	/* create result table */
	lua_newtable(L);

	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {

		if(!strncmp(s->htype,"Function",8)){

			if((!symname)||(strstr(s->symbol, symname))){

				if((!libname)||(strstr(s->libname, libname))){
					scount++;

					if(returnall < 2){

						printf("%s ", strlen(s->libname) ? s->libname : wsh->selflib);
						for (i = strlen(s->libname); i < 40; i++)
							printf(" ");
						printf("%s ", s->symbol);
						for (i = strlen(s->symbol); i < 30; i++)
							printf(" ");
						printf("%s ", s->htype);
						for (i = strlen(s->htype); i < 10; i++)
							printf(" ");
						printf(" %s	%lx	\t\t%lu	%lx\n", s->hbind, s->value, s->size, s->addr);
					}

					/* Add function to Lua table */
					lua_pushstring(L, s->symbol);		/* push key */
					lua_getglobal(L, s->symbol);		/* get pointer to global with this name : keep it as value on top of stack */
					lua_settable(L, -3);

					/* handle breaks via getchar() */
					pcnt++;
//					if((!returnall)&&(pcnt == LINES_MAX)){ pcnt = 0; int c = getchar(); switch(c){case 0x61: pcnt = LINES_MAX + 1; break; case 0x71: return 0; break; default: break;   }; }
				}

			}
		}
	}

	if(returnall < 2){
		printf("\n");
		printf(" -- %u functions matched\n", scount);	
	}

	// Return scount as second return value
	lua_pushinteger(L, scount);

	return 2;	// Return 1 table + number of match
}

/**
* Display objects (typically globals)
*/
int print_objects(lua_State * L)
{
	unsigned int scount = 0;
	symbols_t *s = 0, *stmp = 0;
	unsigned int i = 0;
	unsigned int pcnt = 0;

	char *libname = 0;

	read_arg1(libname);

	DL_COUNT(wsh->symbols, s, scount);
	printf(" -- Total: %u symbols\n", scount);	

	scount = 0;
	printf(" -- Objects:\n\n");
//      printf("    Type       Size                     Path                  Address              Name           (Demangled)\n");
	printf("-----------------------------------------------------------------------------------------------------------------\n");


	DL_FOREACH_SAFE(wsh->symbols, s, stmp) {

		if((!libname)||(strstr(s->libname, libname))){

			if(!strncmp(s->htype,"Object",6)){
				char *sname = 0;
				sname = strlen(s->libname) ? s->libname : wsh->selflib;
				scount++;
				printf("%s ", sname);
				for (i = strlen(sname); i < 40; i++)
					printf(" ");
				printf("%s ", s->symbol);
				for (i = strlen(s->symbol); i < 30; i++)
					printf(" ");
				printf("%s ", s->htype);
				for (i = strlen(s->htype); i < 10; i++)
					printf(" ");
				printf(" %s	%lx	\t\t%lu	%lx\n", s->hbind, s->value, s->size, s->addr);

				pcnt++;
				if(pcnt == LINES_MAX){ pcnt = 0; int c = getchar(); switch(c){case 0x61: pcnt = LINES_MAX + 1; break; case 0x71: return 0; break; default: break;   }; }
			}
		}
	}

	printf("\n");
	printf(" -- %u objects matched\n", scount);	

	return 0;
}

/**
* Display mapped librairies, return a list of library names
*/
int print_libs(lua_State * L)
{
	char *lastlib = "none";
	sections_t *s = 0, *stmp = 0;
	unsigned int scount = 0;

	/* create result table */
	lua_newtable(L);

	DL_FOREACH_SAFE(wsh->phdrs, s, stmp) {
			if(strncmp(lastlib,s->libname,strlen(lastlib))){
				scount++;
				printf("%s\n",s->libname);

				/* Add function to Lua table */
				lua_pushnumber(L, scount);		/* push key */
			        lua_pushstring(L, s->libname);		/* push value */
			        lua_settable(L, -3);
			}
			lastlib = s->libname;
	}

	/**
	* Define vdso
	*/
	printf("%s\n", EXTRA_VDSO);
	scount++;
	// Add function to Lua table //
	lua_pushnumber(L, scount);
	lua_pushstring(L, EXTRA_VDSO);
	lua_settable(L, -3);

	/* add self and vdso as shared library */
	if(wsh->opt_appear){
		// self
		scount++;
		printf("%s\n",wsh->selflib);

		// Add function to Lua table
		lua_pushnumber(L, scount);
	        lua_pushstring(L, wsh->selflib);
	        lua_settable(L, -3);

	}

	printf("\n");
	printf(" -- Total: %u libraries\n", scount);	

	// Return scount as second return value
	lua_pushinteger(L, scount);

	return 2;	// Return 1 table + number of matches
}

/**
* Display ELF sections
*/
int print_shdrs(void)
{
	char *lastlib = "";
	sections_t *s = 0, *stmp = 0;
	unsigned int scount = 0;
	char *segmenttype = "";
	char *segmentperms = "";
	segments_t *seg = 0;

	DL_COUNT(wsh->shdrs, s, scount);

	printf(" -- Total: %u sections\n", scount);	

	DL_FOREACH_SAFE(wsh->shdrs, s, stmp) {
			if(strncmp(lastlib,s->libname,strlen(lastlib))){
				printf("\n");
			}
			lastlib = s->libname;
			char *pcolor = DARKGRAY;	// NORMAL
			switch(s->flags&0x0f){
			case 2:	// r--
				pcolor = GREEN;
				break;
			case 3:	// rw-
				pcolor = BLUE;
				break;
			case 6:	// r-x
				pcolor = RED;
				break;
			case 7:	// rwx
			case 8:	// rwx
			case 9:	// rwx
				pcolor = MAGENTA;
				break;
			default:
				break;
			}

			segmenttype = "";
			segmentperms = "";
			seg = 0;

			seg = segment_from_addr(s->addr);
			if(seg){
				segmenttype = seg->type;
				segmentperms = seg->perms;
			}

			if(wsh->opt_hollywood){
				printf(NORMAL "%012lx-%012lx%s\t%s\t%lu\t%s\t%25s\t%s\t%s" NORMAL "\n", s->addr, s->addr + s->size, pcolor, s->perms, s->size, s->libname, s->name, segmenttype, segmentperms);
			}else{
				printf("%012lx-%012lx\t%s\t%lu\t%s\t%25s\t%s\t%s\n", s->addr, s->addr + s->size, s->perms, s->size, s->libname, s->name, segmenttype, segmentperms);
			}
	}

	printf("\n");
	printf(" -- Total: %u sections\n", scount);	

	return 0;
}

/**
* Display Entry points
*/
int print_eps(void)
{
	eps_t *s = 0, *stmp = 0;
	unsigned int scount = 0;

	DL_COUNT(wsh->eps, s, scount);

	printf(" -- Total: %u entry points\n\n", scount);	

	DL_FOREACH_SAFE(wsh->eps, s, stmp) {
		printf("%012llx\t%s\n", s->addr, s->name);
	}
	return 0;
}

/**
* Sort function helper for sections
*/
int shdr_cmp(sections_t *a, sections_t *b)
{
	return (a->addr - b->addr);
}

/**
* Sort function helper for segments
*/
int phdr_cmp(segments_t *a, segments_t *b)
{
	return (a->addr - b->addr);
}

/**
* Reload linked lists from ELFs binaries
*/
int reload_elfs(void)
{
	empty_eps();

	empty_phdrs();
	dl_iterate_phdr(phdr_callback, NULL);
	DL_SORT(wsh->phdrs, phdr_cmp);

	empty_shdrs();
	dl_iterate_phdr(shdr_callback, NULL);
	DL_SORT(wsh->shdrs, shdr_cmp);

	return 0;
}

/**
* Display section headers (ELF Sections)
*/
static int shdrs(lua_State * L)
{
	print_shdrs();
	return 0;
}


/**
* Display ELF Entry points
*/
int entrypoints(lua_State * L)
{
	print_eps();
	return 0;
}

/**
* Open a manual page
*/
int man(lua_State * L)
{
	void *arg = 0;
	char cmd[255];

	if (lua_isstring(L, 1)) {
		arg = luaL_checkstring(L, 1);
		memset(cmd, 0x00, 255);
		snprintf(cmd, 254, "man %s", (char*) arg);	// Obvious injection. We don't care
		system(cmd);
	}
	return 0;
}


/**
* Display information on an object/memory address
*/
int info(lua_State * L)
{
	void *symbol = 0;
	unsigned long long int ret = 0;
	Dl_info dli;
	char *error = 0;
	Elf_Sym *s = 0;
	unsigned int stype = 0, sbind = 0;
	char *htype = 0, *hbind = 0;

	unsigned long long int n = lua_tonumber(L, 1);

	if(msync(n & ~0xfff, 4096, 0) == 0){	// if read as a number, destination address is mapped

		/**
		* Address is mapped
		*/
	        printf(" * address 0x%llx is mapped\n", n);

		/**
		* Search corresponding symbols
		*/
		symbols_t *sym = symbol_from_addr(n);
		if((sym)&&(sym->addr == n)){
			printf(" * %s %s %s from %s\tsize:%lu\n", sym->hbind, sym->htype, sym->symbol, sym->libname, sym->size);
		}else if(sym){
			printf(" * %llu bytes within %s %s %s from %s\tsize:%lu\n", n - sym->addr, sym->hbind, sym->htype, sym->symbol, sym->libname, sym->size);
		}

		/**
		* Search corresponding section
		*/
		sections_t *sec = section_from_addr(n);
		if(sec){
			printf(" * %llu bytes within %s:%s  %s\n", n - sec->addr, sec->libname, sec->name, sec->perms);
		}

		/**
		* Search corresponding segment
		*/
		sections_t *seg = segment_from_addr(n);
		if(seg){
			printf(" * %llu bytes within %s:%s  %s\n", n - seg->addr, seg->libname, seg->name, seg->perms);
		}

	}else if (lua_isstring(L, 1)) {
		symbol = luaL_checkstring(L, 1);

		/**
		* Search corresponding symbols
		*/
		symbols_t *sym = symbol_from_name(symbol);
		if(!sym){
			printf(" * Symbol %s does not exist\n", (char *)symbol);
			return 0;
		}

		/**
		* Resolve symbol...
		*/
		ret = (unsigned long int) dlsym(wsh->mainhandle, symbol);
		if ((error = dlerror()) != NULL) {
			fprintf(stderr, "ERROR: %s\n", error);
			return 0;
		}

#ifdef __GLIBC__
		if (dladdr1(ret, &dli, (void **) &s, RTLD_DL_SYMENT)&&(s)) {
			stype = ELF_ST_TYPE(s->st_info);
			htype = symbol_totype(stype);

			sbind = ELF_ST_BIND(s->st_info);
			hbind = symbol_tobind(sbind);

			char *secname = "";
			sections_t *sec = section_from_addr(dli.dli_saddr);
			if(sec){
				secname = sec->name;
			}

			printf(" * %s %s %s at %p	%s:%s size:%lu\n", htype, hbind, dli.dli_sname, dli.dli_saddr, dli.dli_fname, secname, s->st_size /*, s->st_value */ );
#else
		if (dladdr(ret, &dli)) {
			stype = 2; // Assume function
			htype = symbol_totype(stype);
			sbind = 1;
			hbind = symbol_tobind(sbind);
			char *secname = "";
			sections_t *sec = section_from_addr(dli.dli_saddr);
			if(sec){
				secname = sec->name;
			}
			printf(" * %s %s %s at %p %s:%s size:%lu\n", htype, hbind, dli.dli_sname, dli.dli_saddr, dli.dli_fname, secname, 0 /* s->st_size */ );
#endif
		} else {
			printf(" * symbol %s does not exist.\n", (char *)symbol);
		}
	} else {
		printf("ERROR: info requires a string argument\n");
	}

	return 0;
}

/**
* Buffer management subroutines
*/

// allocate a char **
int alloccharbuf(lua_State * L)
{
	int n = 0;
	char *ptr = 0;

	n = lua_tonumber(L, 1);
	ptr = calloc(n * sizeof(char *), 1);
	lua_pushnumber(L, (unsigned long int) ptr);
	return 1;
}

// set a pointer within the char **
int setcharbuf(lua_State * L)
{
	char **buff = 0;
	unsigned int pos = 0;
	char *val = 0;

	buff = (unsigned long int) lua_tonumber(L, 1);
	pos = lua_tonumber(L, 2);
	val = lua_tostring(L, 3);

	buff[pos] = val;
	return 0;
}

/**
* Read a string (to a LUA string)
*/
int rdstr(lua_State * L)
{
	char *buff = 0;
	unsigned int n = 0;
	char *val = 0;

	buff = (unsigned long int) lua_tonumber(L, 1);
	n = lua_tonumber(L, 2);
	val = buff;
	if(n){
		lua_pushlstring(L, val, n);	// Push string with length specifier
	}else{
		lua_pushstring(L, val);	// Ascii : push string (no length)
	}
	return 1;
}

/**
* Read a number (to a LUA number)
*/
int rdnum(lua_State * L)
{
	int *buff = 0;
	int val = 0;

	buff = (int) lua_tonumber(L, 1);
	val = buff[0];

	lua_pushnumber(L, (int) val);

	return 1;
}

// read a pointer within the char **
int getcharbuf(lua_State * L)
{
	char **buff = 0;
	unsigned int pos = 0;
	char *val = 0;

	buff = (unsigned long int) lua_tonumber(L, 1);
	pos = lua_tonumber(L, 2);

	val = buff[pos];
	lua_pushstring(L, val);	// Ascii : push string
	return 1;
}


/**
*
* Enable multi-line paste into WSH
*
*/

/**
* Check if syntax error is due to incomplete statement
*/
static int incomplete(lua_State *L, int status)
{
	if (status == LUA_ERRSYNTAX) {
		size_t lmsg;
		const char *msg = lua_tolstring(L, -1, &lmsg);
		const char *tp = msg + lmsg - (sizeof("<eof>") - 1);
		if (strstr(msg, "<eof>") != NULL) {
			lua_pop(L, 1);
			return 1;
		}
	}
	return 0;
}

/**
 * Main function to run a WSH shell
 */
int run_shell(lua_State *L)
{
	if (!isatty(STDIN_FILENO)) {
		/* Non-interactive mode with multiline support */
		char *input_buffer = NULL;
		size_t buffer_size = 0;
		size_t buffer_len = 0;
		char line[4096];	/* Temp buffer for fgets */

		for (;;) {
			if (fgets(line, sizeof(line), stdin) == NULL) {
				/* EOF: try to process any remaining buffer */
				if (buffer_len > 0) {
					int status = luaL_loadbuffer(L, input_buffer, buffer_len, "=(shell)");
					if (status == LUA_OK) {
						/* Execute */
						status = lua_pcall(L, 0, LUA_MULTRET, 0);
						if (status == LUA_OK) {
							/* Print any return values */
							int n = lua_gettop(L);
							if (n > 0) {
								luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
								lua_getglobal(L, "print");
								lua_insert(L, 1);
								if (lua_pcall(L, n, 0, 0) != LUA_OK) {
									fprintf(stderr, "error calling 'print' (%s)\n", lua_tostring(L, -1));
									lua_pop(L, 1);
								}
							}
						} else {
							fprintf(stderr, "ERROR: %s\n", lua_tostring(L, -1));
							lua_pop(L, 1);
						}
						lua_settop(L, 0);
					} else {
						/* Treat incomplete or syntax error at EOF as error */
						fprintf(stderr, "ERROR: %s\n", lua_tostring(L, -1));
						lua_pop(L, 1);
						lua_settop(L, 0);
					}
					buffer_len = 0;
				}
				free(input_buffer);
				return 0;
			}

			/* Trim trailing newline if present */
			size_t line_len = strlen(line);
			if (line_len > 0 && line[line_len - 1] == '\n') {
				line[--line_len] = '\0';
			}

			/* Check for special "cont" to exit */
			if (strcmp(line, "cont") == 0) {
				free(input_buffer);
				return 0;
			}

			/* Allocate or expand buffer */
			size_t needed = buffer_len + line_len + 2;	/* +1 for \n if needed, +1 for \0 */
			if (needed > buffer_size) {
				buffer_size = needed + 1024;
				input_buffer = realloc(input_buffer, buffer_size);
				if (input_buffer == NULL) {
					fprintf(stderr, "Out of memory\n");
					free(input_buffer);
					return 0;
				}
			}

			/* Append line to buffer */
			if (buffer_len > 0) {
				input_buffer[buffer_len++] = '\n';
			}
			memcpy(input_buffer + buffer_len, line, line_len);
			buffer_len += line_len;
			input_buffer[buffer_len] = '\0';

			/* Try to compile */
			int status = luaL_loadbuffer(L, input_buffer, buffer_len, "=(shell)");
			if (status == LUA_OK) {
				/* Success - execute it */
				status = lua_pcall(L, 0, LUA_MULTRET, 0);
				if (status == LUA_OK) {
					/* Print any return values */
					int n = lua_gettop(L);
					if (n > 0) {
						luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
						lua_getglobal(L, "print");
						lua_insert(L, 1);
						if (lua_pcall(L, n, 0, 0) != LUA_OK) {
							fprintf(stderr, "error calling 'print' (%s)\n", lua_tostring(L, -1));
							lua_pop(L, 1);
						}
					}
				} else {
					fprintf(stderr, "ERROR: %s\n", lua_tostring(L, -1));
					lua_pop(L, 1);
				}
				/* Clear buffer for next input */
				buffer_len = 0;
				lua_settop(L, 0);
			} else if (incomplete(L, status)) {
				/* Incomplete input - continue reading */
				continue;
			} else {
				/* Real syntax error */
				fprintf(stderr, "ERROR: %s\n", lua_tostring(L, -1));
				lua_pop(L, 1);
				/* Clear buffer and start over */
				buffer_len = 0;
				lua_settop(L, 0);
			}
		}
	} else {
		/* Interactive mode with multiline support */
		/* Set up history and completion */
		char *SHELL_HISTORY = calloc(1024, 1);
		snprintf(SHELL_HISTORY, 1023, "%s/%s", getenv("HOME"), SHELL_HISTORY_NAME);
		linenoiseHistoryLoad(SHELL_HISTORY);
		linenoiseSetCompletionCallback(completion);
		linenoiseSetMultiLine(1);	/* Enable multiline support */
		lua_settop(L, 0);	// Ignore previous errors in Lua
		/* Buffer for accumulating multiline input */
		char *input_buffer = NULL;
		size_t buffer_size = 0;
		size_t buffer_len = 0;
		while (1) {
			const char *prompt = (buffer_len > 0) ? ">> " : "> ";
			char *line = linenoise(prompt);
			if (line == NULL)
				break;	/* EOF (Ctrl+D) */
			/* Allocate or expand buffer */
			size_t line_len = strlen(line);
			size_t needed = buffer_len + line_len + 2;	/* +1 for \n, +1 for \0 */
			if (needed > buffer_size) {
				buffer_size = needed + 1024;	/* extra space */
				input_buffer = realloc(input_buffer, buffer_size);
				if (input_buffer == NULL) {
					fprintf(stderr, "Out of memory\n");
					free(line);
					break;
				}
			}
			/* Add line to buffer */
			if (buffer_len == 0) {
				strcpy(input_buffer, line);
				buffer_len = line_len;
			} else {
				input_buffer[buffer_len] = '\n';
				strcpy(input_buffer + buffer_len + 1, line);
				buffer_len += line_len + 1;
			}
			free(line);
			/* Try to compile the accumulated input */
			int status = luaL_loadbuffer(L, input_buffer, buffer_len, "=stdin");
			if (status == LUA_OK) {
				/* Success - execute it */
				if (buffer_len > 0) {
					linenoiseHistoryAdd(input_buffer);
				}
				status = lua_pcall(L, 0, LUA_MULTRET, 0);
				if (status == LUA_OK) {
					/* Print any return values */
					int n = lua_gettop(L);
					if (n > 0) {
						luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
						lua_getglobal(L, "print");
						lua_insert(L, 1);
						if (lua_pcall(L, n, 0, 0) != LUA_OK) {
							fprintf(stderr, "error calling 'print' (%s)\n", lua_tostring(L, -1));
							lua_pop(L, 1);
						}
					}
				} else {
					fprintf(stderr, "ERROR: %s\n", lua_tostring(L, -1));
					lua_pop(L, 1);
				}
				/* Clear buffer for next input */
				buffer_len = 0;
				lua_settop(L, 0);
			} else if (incomplete(L, status)) {
				/* Incomplete input - continue reading */
				continue;
			} else {
				/* Real syntax error */
				fprintf(stderr, "ERROR: %s\n", lua_tostring(L, -1));
				lua_pop(L, 1);
				/* Clear buffer and start over */
				buffer_len = 0;
				lua_settop(L, 0);
			}
			linenoiseHistorySave(SHELL_HISTORY);
		}
		/* Cleanup */
		if (input_buffer) {
			free(input_buffer);
		}
		linenoiseHistorySave(SHELL_HISTORY);
		free(SHELL_HISTORY);
	}
	return 0;
}

/**
* Learn function prototypes
*/
int learn_proto(unsigned long*arg, unsigned long int faultaddr, int reason)
{
	char *vreason = 0;
	char *tag = 0;
	long int offset = 0;
	unsigned int i = 0;
	unsigned int argn = 0;
	symbols_t *s = 0;

	if(!reason) { return 0; }		// No error
	if(!faultaddr) { return 0; }		// No Address
	if(faultaddr < 0x1000) { return 0; }	// Address in first page
	if(faultaddr > 0xf000000000000000) { return 0; }	// Address out of userland
	switch(reason){
	case 1:	// read
		vreason = "read";
		tag = "_input_ptr";
		break;
	case 2: // write
		vreason = "write";
		tag = "_output_ptr";
		break;
	case 4: // Exec
		vreason = "exec";
		tag = "_exec_ptr";
		break;
	default:
		return 0;
	}

	for(i=1; i<=7; i++){
		if((faultaddr & ~0xfff) == (arg[i] & ~0xfff)){ argn = i; }
	}

	if(!argn){ return 0; }	// Can't match fault address with any argument

	for(i=1; i<=7; i++){
		if((arg[i] == arg[argn])&&(argn != i)){ return 0; }	// 2 arguments in same page, can't conclude
	}

	offset = faultaddr - arg[argn];

	if(arg[argn] == 0xffff){ return 0; }
	if(arg[argn] == 0x7fff){ return 0; }
	if(arg[argn] == 0xffffffff){ return 0; }
	if(arg[argn] == 0x7fffffff){ return 0; }

	s = symbol_from_addr(arg[0]);

	if(!wsh->learnfile){
		wsh->learnfile = fopen( wsh->learnlog ? wsh->learnlog : DEFAULT_LEARN_FILE ,"a+");
	}

	fprintf(wsh->learnfile, "TAG %s %s argument%u %s %ld\n", s->libname, s->symbol, argn, tag, offset);
	fflush(wsh->learnfile);

	return 0;
}

int sort_learnt(learn_t *a, learn_t *b)
{
	return memcmp(&a->key, &b->key, sizeof(learn_key_t));
}

/**
* Display learned prototypes
*/
int prototypes(lua_State * L)
{
	char *pattern = 0;
	char *patternlib = 0;
	char *patterntag = 0;
	char line[1024];
	learn_t *l = 0, *p = 0;

	read_arg1(pattern);
	read_arg2(patternlib);
	read_arg3(patterntag);

	if(!wsh->learnfile){
		wsh->learnfile = fopen( wsh->learnlog ? wsh->learnlog : DEFAULT_LEARN_FILE ,"a+");
	}

	fseek(wsh->learnfile, 0, SEEK_SET);

	/**
	* Read all the lines to learnt data structure
	*/
	while (fgets(line, sizeof(line), wsh->learnfile)) {
		l = (learn_t*) calloc(1,sizeof(learn_t));

		sscanf(line, "%10s %200s %200s %20s %200s %20s", l->key.ttype, l->key.tlib, l->key.tfunction, l->key.targ, l->key.tvalue, l->toffset);

		// make sure tag type is correct, else discard
		if(strncmp(l->key.ttype, "TAG", 3)){
			printf("WARNING: Unknown TAG type: %s\n", l->key.ttype);
			free(l);
			continue;
		}

		// add to linked list if not present, else free
		HASH_FIND(hh, protorecords, &l->key, sizeof(learn_key_t), p);
		if(p){
			free(l);
		}else{
			HASH_ADD(hh, protorecords, key, sizeof(learn_key_t), l);
		}
	}

	/**
	* Sort learnt data structures
	*/
	HASH_SRT(hh, protorecords, sort_learnt);

	printf("\n [*] Prototypes: (from %u tag informations)\n", HASH_COUNT(protorecords));
	HASH_ITER(hh, protorecords, l, p) {
		if((!patternlib) || (strstr(l->key.tlib, patternlib))){
			if((!pattern) || (!strncmp(pattern, l->key.tfunction, strlen(pattern)))){
				if((!patterntag) || (strstr(l->key.tvalue, patterntag))){
					printf("%s\t%s\t%s\t%s\t%s\n", l->key.tlib, l->key.tfunction, l->key.targ, l->key.tvalue, l->toffset);
				}
			}
		}
	}
	return 0;
}

/*
void enable_trace(void){
	if(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0)){	// Anyone can trace us
		printf("ERROR: prctl() %s\n", strerror(errno));
		return;
	}
}

int pause_on(int syscall_req, int syscall) {
    if(syscall == syscall_req)
        do {
            char buf[2];
            fgets(buf, sizeof(buf), stdin); // waits until enter to continue
        } while(0);
}

#include "reversetrace/syscalls.h"
#include "reversetrace/syscallents.h"

const char *syscall_name(int scn) {
    struct syscall_entry *ent;
    static char buf[128];
    if (scn <= MAX_SYSCALL_NUM) {
        ent = &syscalls[scn];
        if (ent->name)
            return ent->name;
    }
    snprintf(buf, sizeof buf, "sys_%d", scn);
    return buf;
}

long get_register(pid_t child, int off) {
    return ptrace(PTRACE_PEEKUSER, child, 8*off, NULL);
}

long get_syscall_arg(pid_t child, int which) {
    switch (which) {
#ifdef __amd64__
    case 0: return get_register(child, rdi);
    case 1: return get_register(child, rsi);
    case 2: return get_register(child, rdx);
    case 3: return get_register(child, r10);
    case 4: return get_register(child, r8);
    case 5: return get_register(child, r9);
#else
    case 0: return get_register(child, ebx);
    case 1: return get_register(child, ecx);
    case 2: return get_register(child, edx);
    case 3: return get_register(child, esi);
    case 4: return get_register(child, edi);
    case 5: return get_register(child, ebp);
#endif
    default: return -1L;
    }
}

int do_tracer(pid_t child, int syscall_req) {
    int status;
    int retval;

	usleep(100); // Let the child give us permission to trace it via prctl()
	if (ptrace(PTRACE_ATTACH, child, (void *)0L, (void *)0L)) {
	    fprintf(stderr, "Cannot attach to TID %d: %s.\n", child, strerror(errno));
	    return 1;
	}


    waitpid(child, &status, 0);
//	printf("STOPPED\n");

    ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
    while(1) {
        if (wait_for_syscall(child) != 0){
            break;
	}
        print_syscall(child, syscall_req);

        if (wait_for_syscall(child) != 0){
            break;
	}

        retval = get_register(child, eax);

	if((retval >= 0)&&(retval <= 4096)){
	        fprintf(stderr, "%d\n", retval);
	} else if((retval <= -1)&&(retval >= -128)){
	        fprintf(stderr, "%d\n", retval);
	}else{
	        fprintf(stderr, "0x%x\n", retval);
	}
    }
    return 0;
}

int wait_for_syscall(pid_t child) {
    int status;
    while (1) {
        ptrace(PTRACE_SYSCALL, child, 0, 0);
        waitpid(child, &status, 0);
        if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80){
            return 0;
	}
        if (WIFEXITED(status)){
            return 1;
	}
        fprintf(stderr, "[stopped %d (%x)]\n", status, WSTOPSIG(status));
    }
}

void print_syscall_args(pid_t child, int num) {
    struct syscall_entry *ent = NULL;
    int nargs = SYSCALL_MAXARGS;
    int i;
    char *strval;

    if (num <= MAX_SYSCALL_NUM && syscalls[num].name) {
        ent = &syscalls[num];
        nargs = ent->nargs;
    }
    for (i = 0; i < nargs; i++) {
        long arg = get_syscall_arg(child, i);
        fprintf(stderr, "0x%lx", arg);

        if (i != nargs - 1)
            fprintf(stderr, ", ");
    }
}

void print_syscall(pid_t child, int syscall_req) {
    int num;
    num = get_register(child, orig_eax);

    fprintf(stderr, "%s(", syscall_name(num));
    print_syscall_args(child, num);
    fprintf(stderr, ") = ");

    if( syscall_req <= MAX_SYSCALL_NUM) {
        pause_on( num, syscall_req);
    }
}
*/

#ifdef HAS_ASAN
ATTRIBUTE_NO_SANITIZE_ADDRESS
size_t mystrlen(char *ptr)
{
	unsigned int ret = 0;

	while(ptr[ret] != 0){ ret++;}

	return ret;
}

ATTRIBUTE_NO_SANITIZE_ADDRESS
void *mymemset(void *s, int c, size_t n)
{
	unsigned int i = 0;
	char *ptr = 0;

	ptr = s;
	for(i = 0; i < n ; i++){
		ptr[i] = c;
	}

	return s;
}
#else
#define mystrlen strlen
#define mymemset memset
#endif

/**
* Main wrapper around a library call.
* This function returns 9 values: ret (returned by library call), errno, firstsignal, total number of signals, firstsicode, firsterrno, faultaddr, reason, context
*/
static int libcall(lua_State * L)
{
	unsigned long int *arg[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	unsigned int i = 0;
	void *(*f) (void *arg1, void *arg2, void *arg3, void *arg4, void *arg5, void *arg6, void *arg7, void *arg8) = 0;
	void *ret = 0;
	int callerrno = 0;
	int argnum = 0;

	alarm(3);

	for (i = 0; i < 10; i++) {
		if (lua_isnil(L, i + 1)) {
			arg[i] = 0;
		} else if (lua_isnumber(L, i + 1)) {
			arg[i] = (unsigned long) lua_tonumber(L, i + 1);
		} else if (lua_isstring(L, i + 1)) {
			arg[i] = luaL_checkstring(L, i + 1);
		} else if (lua_istable(L, i + 1)) {
		} else if (lua_isfunction(L, i + 1)) {
			arg[i] = lua_tocfunction(L, i + 1);
		} else if (lua_iscfunction(L, i + 1)) {
			arg[i] = lua_touserdata(L, i + 1);
		} else if (lua_isuserdata(L, i + 1)) {
			arg[i] = lua_touserdata(L, i + 1);
		} else {
			arg[i] = 0;
		}
	}

	wsh->firstsignal = 0;
	wsh->firstsicode = 0;
	wsh->totsignals = 0;
	errno = 0;
	wsh->btcaller = 0;
	wsh->firsterrno = 0;
	wsh->faultaddr = 0;
	wsh->reason = 0;

	if (!wsh->errcontext) {
		wsh->errcontext = calloc(1, sizeof(ucontext_t));
	}
//	if (!wsh->initcontext) {
//		wsh->initcontext = calloc(1, sizeof(ucontext_t));
//	}

	memset(wsh->errcontext, 0x00, sizeof(ucontext_t));

//	save_context(wsh->initcontext);	// this is saved to initcontext

	/**
	* Handle (reverse-) system calls tracing
	*/
/*	pid_t parent = 0;
	pid_t child = 0;
	if((wsh->trace_strace)||(wsh->trace_rtrace)){
		enable_trace();
		parent = getpid();
		child = fork();
		enable_trace();

		if (child == 0) {	// child process
			if(wsh->trace_rtrace){
			        return do_tracer(parent, syscall);
			}else{
//			        return do_tracee(argc-push, argv+push);
				printf(" -- tracee pid:%d\n", getpid());
//				kill(getpid(), SIGSTOP);
				goto do_tracee;
			}
		} else {		// parent process
			if(wsh->trace_rtrace){
//			        return do_tracee(argc-push-1, argv+push+1);
				printf(" -- tracee2 pid:%d\n", getpid());
//				kill(getpid(), SIGSTOP);
				goto do_tracee;
			}else{
		        	return do_tracer(child, syscall);
			}
		}
	}
*/
//do_tracee:
	/**
	* Make the library call
	*/
	f = arg[0];
	wsh->interrupted = 0;

	if (!sigsetjmp(wsh->longjmp_ptr, 1)){ // This is executed only the first time // save stack context + signals

		// Set align flag
		if(wsh->trace_unaligned){
			wsh->sigbus_count = 0;
			wsh->sigbus_hash = 0;
			set_align_flag();
		}

		// Set trace flag
		if(wsh->trace_singlestep){
			set_trace_flag();
			wsh->singlestep_count = 0;
			wsh->singlestep_hash = 0;
		}

		// Set branch flag
		if(wsh->trace_singlebranch){
			set_branch_flag();
			wsh->singlebranch_count = 0;
			wsh->singlebranch_hash = 0;
			set_trace_flag();
		}

		ret = f(arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7], arg[8]);
	}else{
//		printf(" + Restored shell execution\n");
		ret = -1;
	}
	unsigned int n = 0, j = 0, notascii = 0;

	// Unset trace flag
	if(wsh->trace_singlestep){
		unset_trace_flag();

		printf("Total: %u instructions traced\n", wsh->singlestep_count);
		printf("Execution hash: ss:%016llx\n", wsh->singlestep_hash);
	}

	// Unset branch flag
	if(wsh->trace_singlebranch){
		unset_trace_flag();
		unset_branch_flag();

		printf("Total: %u blocks traced\n", wsh->singlebranch_count);
		printf("Execution hash: b:%016llx\n", wsh->singlebranch_hash);
	}

	// Unset align flag
	if(wsh->trace_unaligned){
		unset_align_flag();

		printf("Total: %u misaligned access traced\n", wsh->sigbus_count);
		printf("Execution hash: u:%016llx\n", wsh->sigbus_hash);
	}

	callerrno = errno;

	/**
	* Analyse return value
	*/
	n = !msync((long int)ret & (long int)~0xfff, 4096, 0);
	notascii = 0;
	if (n) {
		//      printf("mapped: %p (len:%u)\n", ret, n);
		//      hexdump(ret, n);

		// is it a string ?
		char *ptr = ret;
		for (j = 0; j < mystrlen(ret); j++) {
			if (!isascii((ptr[j]))) {
				notascii = 1;
				break;
			}
		}

#ifdef HAS_ASAN
		if ((!notascii)&&(mystrlen(ret) >= 1)) {
#else
		if (!notascii) {
#endif
			lua_pushstring(L, ret);	// Ascii : push string
		} else {
			lua_pushinteger(L, ret);// Push as a number
		}
	} else {
		lua_pushinteger(L, ret);	// Push as a number
	}

	if (callerrno) {
		fprintf(stderr, "ERROR: %s (%u)\n", strerror(callerrno), callerrno);
	}

	/**
	* Learn prototypes
	*/
	if((wsh->reason)&&(wsh->faultaddr >= 0x40000000)&&(wsh->faultaddr <= 0x7f0000000000)){
		learn_proto(arg, wsh->faultaddr, wsh->reason);
	}

	/**
	*
	* Create output execution context table
	*
	*/
	/* create result table */
	lua_newtable(L);

	/**
	* Push errno to lua table
	*/
	lua_pushstring(L, "errno");		/* push key */
	lua_pushinteger(L, callerrno);
        lua_settable(L, -3);

	/**
	* Push strerror(errno) to lua table
	*/
	lua_pushstring(L, "errnostr");		/* push key */
	lua_pushstring(L, strerror(callerrno));		/* push key */
        lua_settable(L, -3);

	/**
	* Push first signal
	*/
//	lua_pushstring(L, "fsig");		/* push key */
//	lua_pushinteger(L, wsh->firstsignal);
//        lua_settable(L, -3);

	/**
	* Push first signal name
	*/
	lua_pushstring(L, "signal");		/* push key */
	lua_pushstring(L, wsh->firstsignal ? signaltoname(wsh->firstsignal) : "");
        lua_settable(L, -3);

	/**
	* Push total of signals emmited during this libcall
	*/
//	lua_pushstring(L, "nsignals");		/* push key */
//	lua_pushinteger(L, wsh->totsignals);
//        lua_settable(L, -3);

	/**
	* Push first errno
	*/
//	lua_pushstring(L, "ferrno");		/* push key */
//	lua_pushinteger(L, wsh->firsterrno);
//        lua_settable(L, -3);

	/**
	* Push first sicode
	*/
//	lua_pushstring(L, "fcode");		/* push key */
//	lua_pushinteger(L, wsh->firstsicode);
//        lua_settable(L, -3);

	/**
	* Push first sicode name
	*/
	lua_pushstring(L, "sicode");		/* push key */
//	lua_pushinteger(L, wsh->firstsicode);
	siginfo_t *s;
	s = calloc(1, sizeof(siginfo_t));
	s->si_code = wsh->firstsicode;
	lua_pushstring(L, sicode_strerror(wsh->firstsignal, s));		/* push value */
	free(s);
        lua_settable(L, -3);

	/**
	* Address of last caller in backtrace
	*/
	symbols_t *symbt = symbol_from_addr(wsh->btcaller);
	if(symbt){
		lua_pushstring(L, "caller");		/* push key */
		lua_pushstring(L, symbt->symbol);
	        lua_settable(L, -3);
	}

//	if(wsh->btcaller){
		lua_pushstring(L, "calleraddr");		// push key 
//		lua_pushlightuserdata(L, wsh->btcaller);
		lua_pushinteger(L, wsh->btcaller);
		lua_settable(L, -3);
//	}

	/**
	* Push fault address
	*/
	lua_pushstring(L, "faultaddr");		/* push key */
//	if(wsh->faultaddr){
//		lua_pushlightuserdata(L, wsh->faultaddr);
//	}else{
//		lua_pushinteger(L, 0);
//	}
	lua_pushinteger(L, wsh->faultaddr);
        lua_settable(L, -3);

	/**
	* Push reason
	*/
//	lua_pushstring(L, "reason");		/* push key */
//	lua_pushinteger(L, wsh->reason);
//        lua_settable(L, -3);


	/**
	* Push mode
	*/
	lua_pushstring(L, "mode");		/* push key */
	switch(wsh->reason){
	case 1:
		lua_pushstring(L, "READ");		/* push value */
		break;
	case 2:
		lua_pushstring(L, "WRITE");		/* push value */
		break;
	case 4:
		lua_pushstring(L, "EXEC");		/* push value */
		break;
	case 0:
	default:
		lua_pushinteger(L, wsh->reason);	/* push value */
		break;
	}	
        lua_settable(L, -3);

	/** 
	* Push errctx
	*/
//	lua_pushstring(L, "reg");		/* push key */
//	lua_pushinteger(L, wsh->totsignals ? wsh->errcontext : 0);
//        lua_settable(L, -3);

	/**
	* Push pointer to ucontext
	*/
//	lua_pushstring(L, "errctx");		/* push key */
//	lua_pushinteger(L, wsh->errcontext);
//        lua_settable(L, -3);


	/**
	* Push arguments as a new table
	*/

	lua_pushstring(L, "arg");		/* push key */
	lua_createtable(L,6,0);

	argnum = 0;
	for(j=1;j<=6;j++){
		char argname[10];
		memset(argname, 0x00, 10);
		snprintf(argname, 9, "arg%u", j);
		if(arg[j]){
			lua_pushstring(L, argname);		/* push key */
			lua_pushinteger(L, arg[j]);
			lua_settable(L, -3);
			argnum++;
		}
	}

        lua_settable(L, -3);


	/**
	* Push number of non NULL arguments
	*/
	lua_pushstring(L, "argnum");		/* push key */
	lua_pushinteger(L, argnum);
	lua_settable(L, -3);


	/**
	* Push retval
	*/
	lua_pushstring(L, "retval");		/* push key */
	if (n) {
		// is it a string ?
		char *ptr = ret;
		for (j = 0; j < strlen(ret); j++) {
			if (!isascii((ptr[j]))) {
				notascii = 1;
				break;
			}
		}

		if (!notascii) {
			lua_pushstring(L, ret);	// Ascii : push string
		} else {
			lua_pushinteger(L, ret);	// Push as a number
		}
	} else {
		lua_pushinteger(L, ret);	// Push as a number
	}
        lua_settable(L, -3);

	/**
	* Push libcall/libname
	*/
//	lua_pushstring(L,"caller");		/* key */
//	lua_pushinteger(L, arg[0]);
//	lua_settable(L, -3);

	symbols_t *symlib = symbol_from_addr(arg[0]);
	if(symlib){
		lua_pushstring(L,"alibcall");		/* key */
		lua_pushstring(L, symlib->symbol);
		lua_settable(L, -3);

		lua_pushstring(L,"alibname");		/* key */
		lua_pushstring(L, symlib->libname);
		lua_settable(L, -3);
	}



//* This function returns 9 values: ret (returned by library call), errno, firstsignal, total number of signals, firstsicode, firsterrno, faultaddr, reason, context


	/**
	* Invoke store running function on context
	*/

	lua_getglobal(L, "storerun");
	lua_pushvalue(L,-2);
	if(lua_pcall(L, 1, 1, 0)){
		if(wsh->opt_verbose){
			printf("ERROR: calling function storerun() in libcall() %s\n", lua_tostring(L, -1));
		}
	}
	lua_pop(L,1);

	alarm(0);

	return 2;
}

/**
* Append a command to internal lua buffer
*/
int luabuff_append(char *cmd){

	/**
	* Allocate wsh->luabuff if it hasn't been initialized
	*/
	if(!wsh->luabuff){
		wsh->luabuff = calloc(1, 4096);
		wsh->luabuffsz = 4096;
	}

	/**
	* Extend wsh->luabuff by one page if necessary
	*/
	if(strlen(wsh->luabuff) + strlen(cmd) >= wsh->luabuffsz){
		wsh->luabuff = realloc(wsh->luabuff, wsh->luabuffsz + 4096);
		wsh->luabuffsz += 4096;
	}

	/**
	* Append buffer
	*/
	strcat(wsh->luabuff, cmd);

//printf("Appending %s\n", cmd);
	return 0;
}

/**
* Extract just the function name from a fully qualified name
*/
char* extract_function_name(const char* full_name) {
    if (!full_name) return NULL;
    
    char* result = strdup(full_name);
    char* name = result;
    
    // Remove parameters - find first '(' and truncate there
    char* params = strchr(name, '(');
    if (params) {
        *params = '\0';
    }
    
    // Remove template parameters - find first '<' and truncate there
    char* templates = strchr(name, '<');
    if (templates) {
        *templates = '\0';
    }
    
    // For Rust/C++: extract last component after final '::'
    char* last_colon = strstr(name, "::");
    char* function_start = name;
    
    while (last_colon) {
        function_start = last_colon + 2;
        last_colon = strstr(function_start, "::");
    }
    
    // If we found a different start point, move the string
    if (function_start != name) {
        memmove(name, function_start, strlen(function_start) + 1);
    }
    
    return result;
}

/**
* Detect if a given symbol is a Rust symbol
*/
int is_likely_rust_symbol(const char *symbol)
{
	if (!symbol)
		return 0;

	// Definitive Rust v0 mangling
	if (strncmp(symbol, "_R", 2) == 0) {
		return 1;
	}
	// Legacy Rust symbols (Itanium-style but with Rust characteristics)
	if (strncmp(symbol, "_ZN", 3) == 0) {
		// Look for Rust hash suffixes: ::h[16 hex digits]E
		if (strstr(symbol, "17h") && strlen(symbol) > 20) {
			// Check if it ends with 16 hex chars + E
			char *h_pos = strstr(symbol, "17h");
			if (h_pos && strlen(h_pos) >= 19) {
				char *end = h_pos + 3 + 16;	// "17h" + 16 hex chars
				if (*end == 'E' || *(end + 1) == 'E') {
					return 1;
				}
			}
		}
		// Look for common Rust crate names
		if (strstr(symbol, "3std") ||	// std::
		    strstr(symbol, "4core") ||	// core::
		    strstr(symbol, "5alloc") ||	// alloc::
		    strstr(symbol, "4proc") ||	// proc_macro::
		    strstr(symbol, "4test")) {	// test::
			return 1;
		}
		// Look for Rust-specific patterns
		if (strstr(symbol, "4main") ||	// main function
		    strstr(symbol, "5panic") ||	// panic functions
		    strstr(symbol, "6Option") ||	// Option<T>
		    strstr(symbol, "6Result")) {	// Result<T>
			return 1;
		}
	}

	return 0;
}

/**
* Attempt to demangle Rust or C++ functions
*/
char* universal_demangle(const char* symbol) {
    char* demangled = NULL;
    
    if (strncmp(symbol, "_R", 2) == 0 || 
        (strncmp(symbol, "_ZN", 3) == 0 && is_likely_rust_symbol(symbol))) {
        // Rust symbol - always need post-processing
        char* full = rust_demangle(symbol);
        if (full) {
            demangled = extract_function_name(full);
            free(full);
        }
    } else if (strncmp(symbol, "_Z", 2) == 0) {
        // C++ symbol - try different flag combinations
        
        // Try without parameters first
        demangled = cplus_demangle(symbol, DMGL_ANSI);
        
        if (demangled) {
            // Still might have parameters, clean up
            char* cleaned = extract_function_name(demangled);
            free(demangled);
            demangled = cleaned;
        }
    }
    
    return demangled ? demangled : strdup(symbol);
}

void scan_syms(char *dynstr, Elf_Sym * sym, unsigned long int sz, char *libname, unsigned int nsym)
{
    unsigned int cnt = 0;
    char *htype = 0;
    unsigned long int address = 0;
    char *demangled = 0, *symname = 0;
    unsigned int func = 0;
    unsigned int j = 0;
    unsigned skip_bl = 0;
    char newname[1024];
    char *luacmd = 0;

    if (wsh->opt_verbose) {
        printf("    * scan_syms: %s, nsym=%u, sz=%lu\n", libname, nsym, sz);
    }

#ifdef __GLIBC__
    while ((sym)&&(!msync((long unsigned int)sym &~0xfff,4096,0))) {
#else
    // FIXED: Use the provided nsym parameter properly
    for (unsigned int cnt = 0; cnt < nsym && sym; cnt++, sym++) {
#endif
        func = 0;
        if (sym->st_name >= sz) {
            if (wsh->opt_verbose) {
                printf("    * Breaking: st_name (%u) >= sz (%lu)\n", sym->st_name, sz);
            }
            break;
        }
        
        symname = dynstr + sym->st_name;
        
        // Add debug output for first few symbols
        if (wsh->opt_verbose && cnt < 5) {
            printf("    * Symbol[%u]: %s (type: %u, addr: %lx)\n", 
                   cnt, symname, ELF_ST_TYPE(sym->st_info), sym->st_value);
        }

        // Rest of function continues as before...
        switch (ELF_ST_TYPE(sym->st_info)) {
        case STT_GNU_IFUNC:
        case STT_FUNC:
            htype = "Function";
            func = 1;
            break;
        case STT_OBJECT:
            htype = "Object";
            break;
        case STT_SECTION:
            htype = "Section";
            break;
        case STT_FILE:
            htype = "File";
            break;
        case STT_NOTYPE:
        case STT_NUM:
        case STT_LOPROC:
        case STT_HIPROC:
        default:
            htype = 0;
            break;
        }

        if (symname) {
            address = resolve_addr(symname, libname);
        } else {
            address = (unsigned long int) -1;
        }

        demangled = universal_demangle(symname);

        if (strlen(symname) && (htype) && (address != (unsigned long int) -1) && (address)) {
            skip_bl = 0;

            for(j=0; j < sizeof(lua_blacklist)/sizeof(char*);j++){
                if((strlen(symname) == strlen(lua_blacklist[j]))&&(!strncmp(lua_blacklist[j] ,symname, strlen(lua_blacklist[j])))){
                    skip_bl = 1;
                }
            }

            for(j=0; j < sizeof(lua_default_functions)/sizeof(char*);j++){
                if((strlen(symname) == strlen(lua_default_functions[j]))&&(!strncmp(lua_default_functions[j] ,symname, strlen(lua_default_functions[j])))){
                    skip_bl = 1;
                }
            }
            
            if(skip_bl){
#ifdef DEBUG
                printf(" * blacklisted function name: %s\n", symname);
#endif
            } else if (func) {
                memset(newname, 0x00, 1024);
                snprintf(newname, 1023, "reflect_%s", symname);
                lua_pushcfunction(wsh->L, (void *) address);
                lua_setglobal(wsh->L, newname);

                luacmd = calloc(1, 1024);
                snprintf(luacmd,1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", demangled, newname);
                luabuff_append(luacmd);
                free(luacmd);
                scan_symbol(demangled, libname);
            } else {
                if((!scan_symbol(symname, libname))&&(msync(address &~0xfff,4096,0) == 0)) {
                    lua_pushlstring(wsh->L, (char *) address, sym->st_size);
                    lua_setglobal(wsh->L, symname);
                }
            }
        }

        free(demangled);
        
#ifdef __GLIBC__
        cnt++;
        sym++;
#endif
    }
    
    if (wsh->opt_verbose) {
        printf("    * scan_syms complete: processed %u symbols from %s\n", cnt, libname);
    }
}

void parse_dyn(struct link_map *map)
{
	Elf_Dyn *dyn;
	unsigned int cnt = 0;
	unsigned int done = 0;

	char *dynstr = 0;
	Elf_Sym *dynsym = 0;
	unsigned int dynstrsz = 0;
//	char *sec_init = 0;
//	char *sec_fini = 0;
//	char *sec_initarray = 0;
//	unsigned long int sec_initarraysz = 0;
//	char *sec_finiarray = 0;
//	unsigned long int sec_finiarraysz = 0;

	dyn = map->l_ld;

	/**
	* Walk the array of ELF_Dyn once looking for critical sections
	*/
	while ((dyn) && (!done)) {
		cnt++;

		switch (dyn->d_tag) {

		case DT_NULL:
		case DT_NEEDED:
		case DT_HASH:
		case DT_RELA:
		case DT_RELASZ:
		case DT_RELAENT:
		case DT_SYMENT:
		case DT_SONAME:
		case DT_RPATH:
		case DT_SYMBOLIC:
		case DT_REL:
		case DT_RELSZ:
		case DT_RELENT:
		case DT_PLTREL:
		case DT_DEBUG:
		case DT_TEXTREL:
		case DT_JMPREL:
		case DT_NUM:
		case DT_LOPROC:
		case DT_HIPROC:
		case DT_PROCNUM:
		case DT_VERSYM:
		case DT_VERDEF:
		case DT_VERDEFNUM:
		case DT_VERNEED:
		case DT_VERNEEDNUM:
		case 0x6ffffef5:
			break;

		case DT_STRTAB:
			dynstr = (char *) dyn->d_un.d_val;
			break;
		case DT_SYMTAB:
			dynsym = (Elf_Sym *) dyn->d_un.d_val;
			break;
		case DT_STRSZ:
			dynstrsz = dyn->d_un.d_val;
			break;
		case DT_INIT:
//			sec_init = (char *) dyn->d_un.d_val;
			break;
		case DT_FINI:
//			sec_fini = (char *) dyn->d_un.d_val;
			break;
		case DT_INIT_ARRAY:
//			sec_initarray = (char *) dyn->d_un.d_val;
			break;
		case DT_INIT_ARRAYSZ:
//			sec_initarraysz = dyn->d_un.d_val;
			break;
		case DT_FINI_ARRAY:
//			sec_finiarray = (char *) dyn->d_un.d_val;
			break;
		case DT_FINI_ARRAYSZ:
//			sec_finiarraysz = dyn->d_un.d_val;
			break;

		case DT_PLTGOT:
//                      pltgot  = (void *) dyn->d_un.d_val;
			break;
		case DT_PLTRELSZ:
//                      pltsz  = dyn->d_un.d_val / 16;
			break;
		default:
			done = 1;
			break;
		}
		dyn += 1;
	}
	scan_syms(dynstr, dynsym, dynstrsz, map->l_name, 0);
}

#ifndef __GLIBC__
static int sym_callback(struct dl_phdr_info *info, size_t size, void *data)
{
    const char *libname = info->dlpi_name;
    
    // Handle musl's behavior where main program has empty/short name
    if ((!libname) || (strlen(libname) < 2)) {
        if (info->dlpi_addr != 0) {
            // This is likely the main program on musl
            libname = wsh->selflib ? wsh->selflib : "/proc/self/exe";
            if (wsh->opt_verbose) {
                printf("  * Adjusted empty libname to: %s\n", libname);
            }
        } else {
            if (wsh->opt_verbose) {
                printf("  * Skipping: empty name and zero addr\n");
            }
            return 0; // Skip only if it's truly empty and addr is 0
        }
    }
    
    // FIX: Skip processing our own binary to avoid "Resource busy" error
    if (wsh->selflib && libname && !strcmp(libname, wsh->selflib)) {
        if (wsh->opt_verbose) {
            printf("  * Skipping self-processing: %s\n", libname);
        }
        return 0; // Continue iteration but skip ourselves
    }
    
    // Also skip if it looks like wsh
    if (libname && (strstr(libname, "/wsh") || !strcmp(libname, "wsh"))) {
        if (wsh->opt_verbose) {
            printf("  * Skipping wsh binary: %s\n", libname);
        }
        return 0;
    }

    if (wsh->opt_verbose) {
        printf("  * sym_callback: processing %s (base: %p, phnum: %d)\n", 
               libname, (void*)info->dlpi_addr, info->dlpi_phnum);
    }
    
    // Find PT_DYNAMIC segment
    Elf_Dyn *dyn = NULL;
    for (int j = 0; j < info->dlpi_phnum; j++) {
        if (info->dlpi_phdr[j].p_type == PT_DYNAMIC) {
            dyn = (Elf_Dyn *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
            break;
        }
    }
    
    if (!dyn) {
        if (wsh->opt_verbose) {
            printf("    * No PT_DYNAMIC segment found in %s\n", libname);
        }
        return 0; // Continue iteration
    }
    
    // Parse dynamic section to find symbol table info
    char *dynstr = NULL;
    Elf_Sym *dynsym = NULL;
    unsigned int dynstrsz = 0;
    Elf_Word *hash = NULL;
    Elf_Word *gnu_hash = NULL;
    unsigned int nsym = 0;
    
    // Parse dynamic tags
    Elf_Dyn *dyn_iter = dyn;
    for (; dyn_iter->d_tag != DT_NULL; dyn_iter++) {
        switch (dyn_iter->d_tag) {
            case DT_STRTAB:
                dynstr = (char *) (info->dlpi_addr + dyn_iter->d_un.d_ptr);
                break;
            case DT_SYMTAB:
                dynsym = (Elf_Sym *) (info->dlpi_addr + dyn_iter->d_un.d_ptr);
                break;
            case DT_STRSZ:
                dynstrsz = dyn_iter->d_un.d_val;
                break;
            case DT_HASH:
                hash = (Elf_Word *) (info->dlpi_addr + dyn_iter->d_un.d_ptr);
                break;
            case DT_GNU_HASH:
                gnu_hash = (Elf_Word *) (info->dlpi_addr + dyn_iter->d_un.d_ptr);
                break;
        }
    }
    
    // Calculate number of symbols
    if (hash) {
        // Traditional SysV hash table
        nsym = hash[1]; // nchain = number of symbols
        if (wsh->opt_verbose) {
            printf("    * Using SysV hash: nsym=%u\n", nsym);
        }
    } else if (gnu_hash && dynsym && dynstr) {
        // GNU hash table - more complex calculation
        /*
         * GNU hash table format:
         * uint32_t nbuckets
         * uint32_t symoffset  (index of first symbol in hash table)
         * uint32_t bloom_size
         * uint32_t bloom_shift
         * uint64_t bloom[bloom_size]
         * uint32_t buckets[nbuckets]
         * uint32_t chain[]
         */
        
        uint32_t nbuckets = gnu_hash[0];
        uint32_t symoffset = gnu_hash[1];
        uint32_t bloom_size = gnu_hash[2];
        
        // Bounds checking
        if (nbuckets > 0 && nbuckets < 100000 && symoffset < 100000 && bloom_size < 10000) {
            // Find the highest symbol index by scanning buckets
            uint32_t *buckets = &gnu_hash[4 + bloom_size * 2]; // Skip bloom filter
            uint32_t *chains = &buckets[nbuckets];
            
            uint32_t max_sym_idx = symoffset;
            
            for (uint32_t i = 0; i < nbuckets; i++) {
                if (buckets[i] != 0 && buckets[i] >= symoffset) {
                    uint32_t chain_idx = buckets[i];
                    
                    // Follow the chain to find the last symbol
                    while (chain_idx >= symoffset && chain_idx < 100000) { // Sanity check
                        if (chain_idx > max_sym_idx) {
                            max_sym_idx = chain_idx;
                        }
                        
                        // Check if this is the last symbol in the chain
                        uint32_t chain_val = chains[chain_idx - symoffset];
                        if (chain_val & 1) {
                            break; // Last symbol in chain
                        }
                        chain_idx++;
                    }
                }
            }
            
            nsym = max_sym_idx + 1;
            
            if (wsh->opt_verbose) {
                printf("    * GNU hash: nbuckets=%u, symoffset=%u, bloom_size=%u, max_sym_idx=%u, nsym=%u\n",
                       nbuckets, symoffset, bloom_size, max_sym_idx, nsym);
            }
        } else {
            // Invalid GNU hash, try fallback
            nsym = 0;
            if (wsh->opt_verbose) {
                printf("    * Invalid GNU hash parameters, using fallback\n");
            }
        }
    }
    
    // Fallback calculation if we couldn't get nsym from hash tables
    if (nsym == 0 && dynsym && dynstr) {
        // Try to calculate from available info
        // Look for the next section after dynsym to estimate size
        char *dynsym_end = dynstr;  // String table usually follows symbol table
        
        if (dynsym_end > (char*)dynsym) {
            size_t dynsym_size = dynsym_end - (char*)dynsym;
            nsym = dynsym_size / sizeof(Elf_Sym);
        } else {
            // Last resort: use string table size as rough estimate
            nsym = dynstrsz / 20; // Very rough estimate (avg symbol name length)
        }
        
        if (wsh->opt_verbose) {
            printf("    * Fallback calculation: dynsym=%p, dynstr=%p, estimated nsym=%u\n",
                   dynsym, dynstr, nsym);
        }
    }
    
    // Debug output
    if (wsh->opt_verbose) {
        printf("    * dynstr: %p, dynsym: %p, dynstrsz: %u, nsym: %u\n", 
               dynstr, dynsym, dynstrsz, nsym);
        printf("    * hash: %p, gnu_hash: %p\n", hash, gnu_hash);
    }
    
    // Validate we have what we need and the values are reasonable
    if (dynstr && dynsym && dynstrsz > 0 && nsym > 0 && nsym < 1000000) {
        if (wsh->opt_verbose) {
            printf("  * Parsing symbols from %s (base: %p, symbols: %u)\n", 
                   libname, (void*)info->dlpi_addr, nsym);
        }
        scan_syms(dynstr, dynsym, dynstrsz, (char *) libname, nsym);
    } else {
        if (wsh->opt_verbose) {
            printf("  * Skipping %s - invalid symbol info (dynstr:%p dynsym:%p dynstrsz:%u nsym:%u)\n",
                   libname, dynstr, dynsym, dynstrsz, nsym);
        }
    }
    
    // Always return 0 to continue iteration
    return 0;
}
#endif

#ifdef __GLIBC__
int parse_link_map_dyn(struct link_map *map)
{
	if (!map) {
		if ((!wsh->opt_quiet)&&(!wsh->userland_loaded)) {
			fprintf(stderr, "WARNING: No binary loaded in memory. Try loadbin(). For help type help(\"loadbin\").\n");
		}
		return -1;
	}

	// go to first in linked list...
	while ((map) && (map->l_prev)) {
		map = map->l_prev;
	}

	// skip first entries in an attempt to not display the libs we load for ourselves...
	if(!wsh->opt_appear){
		if (map->l_next) {	// Leave libdl.so apparent in verbose mode
			map = map->l_next;
		}
		if (map->l_next) {	// expose ourselved
			map = map->l_next;
		}
	}

	while (map) {
		parse_dyn(map);
		map = map->l_next;
	}

	return 0;
}
#else
int parse_link_map_dyn(struct link_map *map) {
    if (wsh->opt_verbose) {
        printf("  * parse_link_map_dyn (musl): processing loaded modules\n");
    }
    
    // Portable: Use dl_iterate_phdr to parse symbols from all loaded modules
    dl_iterate_phdr(sym_callback, NULL);
    return 0;
}
#endif

/**
* Execute internal lua buffer
*/
int exec_luabuff(void)
{
	char *buff = 0;
	unsigned int n = 0;
	char line[4096];

	int err = 0;

	if(wsh->luabuffsz == 0){ return 0; }


	/**
	* Load buffer in lua
	*/
	if ((err = luaL_loadbuffer(wsh->L, wsh->luabuff, strlen(wsh->luabuff), "=Wsh internal lua buffer")) != 0) {
		if (wsh->opt_verbose) {
			printf("WARNING: Wsh internal lua initialization (%s): %s\n", lua_strerror(err), lua_tostring(wsh->L, -1));
		}
		//
		// Run line by line instead...
		//
		memset(line, 0x00, 4096);

		buff = wsh->luabuff;
		while (sscanf(buff, "%[^\n]\n%n", line, &n) == 1) {		    
			if ((err = luaL_loadbuffer(wsh->L, line, strlen(line), "=Wsh internal lua buffer")) != 0) {
				goto skipthisone;
			}
		    
	    		if(lua_pcall(wsh->L, 0, 0, 0)){
				fprintf(stderr, "WARNING: lua_pcall() failed with %s\n",lua_tostring(wsh->L, -1));
			}
skipthisone:
			buff += n;            
			memset(line, 0x00, 4096);
		}

		free(wsh->luabuff);
		wsh->luabuff = 0;
		wsh->luabuffsz = 0;

		return 0;
	}

	/**
	* Execute buffer
	*/
	if(lua_pcall(wsh->L, 0, 0, 0)){
		fprintf(stderr, "WARNING: lua_pcall() failed with %s\n",lua_tostring(wsh->L, -1));
	}

	/**
	* Release internal lua buffer
	*/
	free(wsh->luabuff);
	wsh->luabuff = 0;
	wsh->luabuffsz = 0;

	return 0;
}

void parse_link_vdso(void)
{
	// add extra vdso
	struct link_map *vdso = 0;
	vdso = dlopen(EXTRA_VDSO, wsh->opt_global ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW);
	if(wsh->opt_verbose){ printf(" -- Adding extra (arch specific) vdso library: %s at %p\n", EXTRA_VDSO, vdso); }
	parse_link_map_dyn(vdso);
}

/**
* Rescan address space
*/
void rescan(void)
{
    if (wsh->opt_verbose) {
        printf("  * rescan() called\n");
    }
    
    reload_elfs();

    empty_symbols();
    wsh->opt_rescan = 1;
    
    if (wsh->opt_verbose) {
        printf("  * About to call parse_link_map_dyn\n");
    }
    
    parse_link_map_dyn(wsh->mainhandle);
    parse_link_vdso();
    wsh->opt_rescan = 0;
    exec_luabuff();
    
    if (wsh->opt_verbose) {
        printf("  * rescan() complete\n");
    }
}


/**
* Display content of /proc/self/maps
*/
int print_procmap(unsigned int pid)
{
	char path[100];
	int n = 0;
	int fd = 0;
	char *buff = 0;

	memset(path, 0x00, sizeof(path));
	snprintf(path, 99, "/proc/%u/maps", pid);
	buff = calloc(1, 4096);
	fd = open(path, O_RDONLY);
	if(fd < 0){ printf("ERROR: open %s : %s\n", path, strerror(errno)); return -1; }

	while ((n = read(fd, buff, 4096)) > 0){
		write(1, buff, n);
		memset(buff, 0x00, 4096);
	}
	free(buff);
	close(fd);

	return 0;
}

int procmap_lua(void)
{
	return print_procmap(getpid());
}

int execlib(lua_State * L)
{
	int ret = 0;
	int child = 0;
	int status = 0;
	int pid = 0;
	siginfo_t si;

	child = fork();
	if (child == 0) {	// child
		ptrace(PTRACE_TRACEME, 0, 0, 0);

		ret = libcall(L);
		_Exit(EXIT_SUCCESS);
	} else if (child == -1) {
		fprintf(stderr, "ERROR: fork() : %s\n", strerror(errno));
		_Exit(EXIT_FAILURE);
	} else {		// parent
		ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEEXIT);

		while (1) {
			if (waitpid(-1, &status, __WALL) == -1) {
				printf(" [*] traced process exited with status %d\n", WEXITSTATUS(status));
				_Exit(EXIT_FAILURE);
			}
			if (WIFSTOPPED(status)) {
				pid = child;	// save child's pid
				if (ptrace(PTRACE_CONT, child, 0, 0) == -1) {
					fprintf(stderr, "ERROR: ptrace() : %s\n", strerror(errno));
					_Exit(EXIT_FAILURE);
				}
				// check return signal/error code
				ptrace(PTRACE_GETSIGINFO, pid, NULL, &si);
				if (si.si_signo || si.si_errno || si.si_code) {
					printf("[*] Child stopped with signal: %i" " errno: %i code: %i\n", si.si_signo, si.si_errno, si.si_code);
					break;
				}
			}
		}
	}

	return 0;
}

int traceback(lua_State * L)
{
	lua_getglobal(L, "debug");
	lua_getfield(L, -1, "traceback");
	lua_pushvalue(L, 1);
	lua_pushinteger(L, 2);
	lua_call(L, 2, 1);
	printf("%s\n", lua_tostring(L, -1));

	return 1;
}

void print_backtrace(void)
{
	void *traceptrs[100];
	char **funcnames = 0;
	size_t count = 0;
	unsigned int i = 0;
	char *p = 0;

	count = backtrace(traceptrs, 100);
	funcnames = backtrace_symbols(traceptrs, count);
	if (count > SKIP_BOTTOM) {
		count -= SKIP_BOTTOM;
	}
	for (i = SKIP_INIT; i < count; i++) {
		// truncate at first space
		p = strchr(funcnames[i], 0x20);
		if (p) {
			p[0] = 0x00;
		}
		printf("\t%p    %s\n", traceptrs[i], funcnames[i]);
	}
	free(funcnames);
}

char *sicodetoname(int code)
{
	return "Unknown";
}

char *signaltoname(int signal)
{
	unsigned int i;
	for (i = 0; i < sizeof(signames) / sizeof(signame_t); i++) {
		if (signames[i].signal == signal) {
			return signames[i].name;
		}
	}

	return "Unknown Signal";
}


inline void unset_align_flag(void)
{
#ifdef __amd64__
	// Unset Align flag
	asm(".intel_syntax noprefix;"
		"pushf;"
		"pop rax;"
		"xor rax, 0x40000;"
		"push rax;"
		"popf;"
	);
#endif
}


inline void set_align_flag(void)
{
#ifdef __amd64__
	// Set Align flag
	asm(".intel_syntax noprefix;"
		"pushf;"
		"pop rax;"
		"or rax, 0x40000;"
		"push rax;"
		"popf;"
	);
#endif
}

inline void unset_trace_flag(void)
{
#ifdef __amd64__
	// Unset trace flag
	asm(".intel_syntax noprefix;"
		"pushf;"
		"pop rax;"
		"xor rax, 0x100;"
		"push rax;"
		"popf;"
	);
#endif
}


inline void set_trace_flag(void)
{
#ifdef __amd64__
	// Set Trace flag
	asm(".intel_syntax noprefix;"
		"pushf;"
		"pop rax;"
		"or rax, 0x100;"
		"push rax;"
		"popf;"
	);
#endif
}

/**
* Set affinity of a thread to a given CPU
*/
void affinity(int procnum)
{
	cpu_set_t set;

	CPU_ZERO(&set);
	CPU_SET(procnum, &set);

        if (sched_setaffinity(getpid(), sizeof(set), &set) == -1){
		fprintf(stderr, "ERROR: sched_setaffinity(%u): %s\n", procnum, strerror(errno));
	}
}

/**
* Enable Branch Tracing
*/
void btr_enable(int procnum)
{
	char cpupath[200];
	uint64_t data = 0x02;
	int fd = 0, ret = 0;

	memset(cpupath, 0x00, 200);
	snprintf(cpupath, 199, "/dev/cpu/%d/msr", procnum);
	fd = open(cpupath, O_WRONLY);
	if(fd <= 0){ fprintf(stderr, "ERROR: open(%s): %s\n", cpupath,strerror(errno)); return; }
	ret = lseek(fd, 0x00, SEEK_SET);
	if(ret != 0x00){ fprintf(stderr, "ERROR: lseek(): %s\n", strerror(errno)); return; }
	ret = pwrite(fd, &data, sizeof(data), 0x1d9);
	if(ret != sizeof(data)){ fprintf(stderr, "ERROR: write(): %s\n", strerror(errno)); return; }
	ret = close(fd);
	if(ret != 0){ fprintf(stderr, "ERROR: close(): %s\n", strerror(errno)); return; }
}

/**
* Disable Branch Tracing
*/
void btr_disable(int procnum)
{
	char cpupath[200];
	uint64_t data = 0x00;
	int fd = 0, ret = 0;

	memset(cpupath, 0x00, 200);
	snprintf(cpupath, 199, "/dev/cpu/%d/msr", procnum);
	fd = open(cpupath, O_WRONLY);
	if(fd <= 0){ fprintf(stderr, "ERROR: open(%s): %s\n", cpupath,strerror(errno)); return; }
	ret = lseek(fd, 0x00, SEEK_SET);
	if(ret != 0x00){ fprintf(stderr, "ERROR: lseek(): %s\n", strerror(errno)); return; }
	ret = pwrite(fd, &data, sizeof(data), 0x1d9);
	if(ret != sizeof(data)){ fprintf(stderr, "ERROR: write(): %s\n", strerror(errno)); return; }
	ret = close(fd);
	if(ret != 0){ fprintf(stderr, "ERROR: close(): %s\n", strerror(errno)); return; }
}


inline void set_branch_flag(void)
{
/*
//
// The following code only works in ring0
//

	// Enable LBR
	asm(".intel_syntax noprefix;"
	"xor rdx, rdx;"
	"xor rax, rax;"
	"inc rax;"
	"mov rcx, 0x1d9;"
	"wrmsr;"
	);
*/

	// set affinity to processor MY_CPU
	affinity(MY_CPU);

	// enable Branch Tracing (BTR) via msr on processor MY_CPU
	btr_enable(MY_CPU);
}

inline void unset_branch_flag(void)
{

	// disable btranch tracing
	btr_disable(MY_CPU);
}

/**
* SIGBUS handler
*/
void bushandler(int signal, siginfo_t * s, void *ptr)
{
	ucontext_t *u = (ucontext_t *) ptr;

	unset_align_flag();
/*
//
// The faulty address is NOT passed on to the user via si->si_addr :
//
//http://lxr.free-electrons.com/source/arch/x86/kernel/traps.c#L217
//
//

	unsigned int fault = 0;
	char *hfault = "";

	if (u->uc_mcontext.gregs[REG_ERR] & 0x2) {
		fault = FAULT_WRITE;	// Write fault
		hfault = "WRITE";
	} else if (s->si_addr == u->uc_mcontext.gregs[REG_RIP]) {
		fault = FAULT_EXEC;	// Exec fault
		hfault = "EXEC";
	} else {
		fault = FAULT_READ;	// Read fault
		hfault = "READ";
	}

	printf(" -- SIGBUS: %llx\t%llx:%s\n", u->uc_mcontext.gregs[REG_RIP], s->si_addr, hfault);
*/

#if defined(__i386__) || defined(__amd64__)
	if(wsh->trace_unaligned){
		if(wsh->opt_verbosetrace){
			symbols_t *s = symbol_from_addr(u->uc_mcontext.gregs[REG_RIP]);
			if(s){
				fprintf(stderr, " -- SIGBUS[%03u] %llx\t%s()+%llu\t%s\n", wsh->sigbus_count+1, u->uc_mcontext.gregs[REG_RIP], s->symbol, u->uc_mcontext.gregs[REG_RIP] - s->addr, s->libname);
			}else{
				fprintf(stderr, " -- SIGBUS[%03u] %llx\n", wsh->sigbus_count+1, u->uc_mcontext.gregs[REG_RIP]);
			}
		}

		wsh->sigbus_count++;

		wsh->sigbus_hash = (wsh->sigbus_hash >> 2) ^ (~u->uc_mcontext.gregs[REG_RIP]);

		u->uc_mcontext.gregs[REG_EFL] ^= 0x40000;	// Unset Align flag
		u->uc_mcontext.gregs[REG_EFL]|= 0x100;		// Set Trace flag
	}
#endif
}


void alarmhandler(int signal, siginfo_t * s, void *u)
{
	write(1, BLUE, strlen(BLUE));
	write(1, "\n[SIGALRM]\tTimeout",18);
	write(1, NORMAL, strlen(NORMAL));
	write(1, "\n", 1);
	errno = ECANCELED;
	alarm(1);
	longjmp(wsh->longjmp_ptr, 1);
}

void inthandler(int signal, siginfo_t * s, void *u)
{
	write(1, MAGENTA, strlen(MAGENTA));
	write(1, "\n[SIGINT]\tInterrupted",21);
	write(1, NORMAL, strlen(NORMAL));
	write(1, "\n", 1);
	errno = ECANCELED;
	if(wsh->interrupted++ < 2){
		alarm(1);
		longjmp(wsh->longjmp_ptr, 1);		// Soft interrupt
	}else{
		alarm(0);
		longjmp(wsh->longjmp_ptr_high, 1);	// Hard interrupt
	}
}

int mk_backtrace(void)
{
	void *bt[20];
	int bt_size;
	char **bt_syms;
	int i;

	bt_size = backtrace(bt, 20);
	bt_syms = backtrace_symbols(bt, bt_size);

	for (i = 2; i < bt_size; i++) {
		write(1, "    ", 4);
		write(1, bt_syms[i], strlen(bt_syms[i]));
		write(1, "\n", 1);
	}
	free(bt_syms);

	return 0;
}

/**
* generic function to restore from exit()
*/
void restore_exit(void)
{
	errno = ECANCELED;
	longjmp(wsh->longjmp_ptr, 1);
}
/*
void exit(int status)
{
	fprintf(stderr, " + Called exit(%d), restoring...\n", status);
	restore_exit();
}

//#if defined(__i386__) || defined(__amd64__)
void _exit(int status)
{
	fprintf(stderr, " + Called _exit(%d), restoring...\n", status);
	restore_exit();
}
//#endif
*/
void exit_group(int status)
{
	fprintf(stderr, " + Called exit_group(%d), restoring...\n", status);
	restore_exit();
}


int printarg(unsigned long int val)
{
	if(msync(val &~0xfff,4096,0) == 0){ // Mapped
		int nlen = 0, noflag = 0, k = 0;
		char *ptrx = 0;
		noflag = 0;
		ptrx = val;
		nlen = strnlen(ptrx, 4096 - ((unsigned long int)ptrx & ~0xfff));
		if(nlen){
			for(k=0;k<nlen;k++){ if(!isprint(ptrx[k] & 0xff)){ /*printf(" not printable: ptrx[k] = %02x\n", ptrx[k] & 0xff);*/ ;noflag = 1; }; }
		}
		if(!noflag){
			fprintf(stderr,"\"%s\"", ptrx);
		}else{
			fprintf(stderr,"0x%lx", val);
		}
	}else{
		fprintf(stderr,"0x%lx", val);
	}

	return 0;
}

void traphandler(int signal, siginfo_t * s, void *ptr)
{
	unsigned int i = 0;
	char *ptrd = 0x00;
	ucontext_t *u = 0;
	unsigned int fault = 0;
	char *hfault = 0;
	char *signame = 0;

	u = (ucontext_t *) ptr;

	if(wsh->trace_singlebranch){	// Stop tracing ourselves
		unset_branch_flag();
//		u->uc_mcontext.gregs[REG_EFL] ^= 0x100;		// Set Trace flag
	}

#if defined(__i386__) || defined(__amd64__)
	/**
	* Search corresponding Breakpoint
	*/
	for (i = 0; i < wsh->bp_num; i++) {
		if (wsh->bp_array[i].ptr == (char*)(u->uc_mcontext.gregs[REG_RIP] - 1)) {
			printf(" ** EXECUTED BREAKPOINT[%u] at %llx	weight:%u	<", i + 1, u->uc_mcontext.gregs[REG_RIP] - 1, wsh->bp_array[i].weight);
			info_function(u->uc_mcontext.gregs[REG_RIP] - 1);
			ptrd = u->uc_mcontext.gregs[REG_RIP] - 1;
			ptrd[0] = wsh->bp_array[i].backup;
			wsh->bp_points += wsh->bp_array[i].weight;

			// Update bp_points
			lua_pushnumber(wsh->L, wsh->bp_points);
			lua_setglobal(wsh->L, "bp_points");

		}
	}

	if (ptrd) {
		/**
		* This is a breakpoint
		*/

		printf(" ** Restoring execution from  %p\n", ptrd);
		u->uc_mcontext.gregs[REG_RIP]--;	// TODO : decrease by full instruction size

	} else if(wsh->trace_singlebranch) {
		/**
		* We are single branching
		*/

		if((u->uc_mcontext.gregs[REG_RIP] & ~0xffffff) != ((long long int)traphandler & ~0xffffff)){	// Make sure we are not tracing ourselves
			if(wsh->opt_verbosetrace){
				symbols_t *s = symbol_from_addr(u->uc_mcontext.gregs[REG_RIP]);
				if((s)&&(u->uc_mcontext.gregs[REG_RIP] == (long long int)s->addr)){
					fprintf(stderr, " -- Branch[%03d] = 0x%llx\t%s(", wsh->singlebranch_count + 1, u->uc_mcontext.gregs[REG_RIP], s->symbol);
#ifdef DEBUG
#ifdef __amd64__
					printarg(u->uc_mcontext.gregs[REG_RDI]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_RSI]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_RDX]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_RCX]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_R8]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_R9]);
#endif
#endif

					fprintf(stderr, ")\t%s\n", s->libname);
				}else if(s){
					fprintf(stderr, " -- Branch[%03d] = 0x%llx\t%s()+%llu\t%s\n", wsh->singlebranch_count + 1, u->uc_mcontext.gregs[REG_RIP], s->symbol,
						u->uc_mcontext.gregs[REG_RIP] - s->addr, s->libname);
				}else{
					fprintf(stderr, " -- Branch[%03d] = 0x%llx\n", wsh->singlebranch_count + 1, u->uc_mcontext.gregs[REG_RIP]);
				}
			}

			wsh->singlebranch_hash = (wsh->singlebranch_hash >> 2) ^ (~u->uc_mcontext.gregs[REG_RIP]);
			wsh->singlebranch_count++;
		}

		set_branch_flag();
		u->uc_mcontext.gregs[REG_EFL] |= 0x100;		// Set Trace flag
		return ;

	} else if(wsh->trace_singlestep) {
		/**
		* We are single stepping
		*/

		if((u->uc_mcontext.gregs[REG_RIP] & ~0xffffff) != ((long long int)traphandler & ~0xffffff)){	// Make sure we are not tracing ourselves
			if(wsh->opt_verbosetrace){
				symbols_t *s = symbol_from_addr(u->uc_mcontext.gregs[REG_RIP]);

				if((s)&&(u->uc_mcontext.gregs[REG_RIP] == (long long int)s->addr)){
					fprintf(stderr, " -- Step[%03d] = 0x%llx\t%s(", wsh->singlebranch_count + 1, u->uc_mcontext.gregs[REG_RIP], s->symbol);
#ifdef DEBUG
#ifdef __amd64__

					printarg(u->uc_mcontext.gregs[REG_RDI]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_RSI]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_RDX]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_RCX]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_R8]);
					fprintf(stderr, ", ");
					printarg(u->uc_mcontext.gregs[REG_R9]);
#endif
#endif
					fprintf(stderr, ")\t%s\n", s->libname);
				}else if(s){
					fprintf(stderr, " -- Step[%03d] = 0x%llx\t%s()+%llu\t%s\n", wsh->singlestep_count + 1, u->uc_mcontext.gregs[REG_RIP], s->symbol, u->uc_mcontext.gregs[REG_RIP] - s->addr, s->libname);
				}else{
					fprintf(stderr, " -- Step[%03d] = 0x%llx\n", wsh->singlestep_count + 1, u->uc_mcontext.gregs[REG_RIP]);
				}

			}
			wsh->singlestep_count++;
			wsh->singlestep_hash = (wsh->singlestep_hash >> 2) ^ (~u->uc_mcontext.gregs[REG_RIP]);
			u->uc_mcontext.gregs[REG_EFL] |= 0x100;		// Set Trace flag
		}
		return ;
	} else if(wsh->trace_unaligned) {
		/**
		* We are tracing unaligned access via SIGBUS, single step once
		*/
		u->uc_mcontext.gregs[REG_EFL] |= 0x40000;	// Set Align flag
		u->uc_mcontext.gregs[REG_EFL] ^= 0x100;		// Unset Trace flag
		return;
	} else {

		/**
		* This is an unhandled exception : exit
		*/
		if (u->uc_mcontext.gregs[REG_ERR] & 0x2) {
			fault = FAULT_WRITE;	// Write fault
			hfault = "WRITE";
		} else if (s->si_addr == (void *)u->uc_mcontext.gregs[REG_RIP]) {
			fault = FAULT_EXEC;	// Exec fault
			hfault = "EXEC";
		} else {
			fault = FAULT_READ;	// Read fault
			hfault = "READ";
		}
		signame = signaltoname(signal);

		fprintf(stderr, "%s\t(%u)\trip:%llx	%s\t%p\t", signame, signal, u->uc_mcontext.gregs[REG_RIP], hfault, s->si_addr);
		psiginfo(s, "");
		print_backtrace();
		printf(" -- No corresponding breakpoint (among %u), exiting\n", wsh->bp_num);
		_Exit(EXIT_SUCCESS);
	}

#endif
	if (signal) {
		if (!wsh->firstsignal) {
			wsh->firstsignal = signal;
		}
		wsh->totsignals += 1;
	}
}

char *sicode_strerror(int signal, siginfo_t * s)
{
	char *sicode = 0;

	switch (signal) {
	case SIGBUS:
		switch (s->si_code) {
		case BUS_ADRALN:
			sicode = "invalid address alignment";
			break;
		case BUS_ADRERR:
			sicode = "non-existent physical address";
			break;
		case BUS_OBJERR:
			sicode = "object specific hardware error";
			break;
		}
		break;
	case SIGCHLD:
		switch (s->si_code) {
		case CLD_EXITED:
			sicode = "child has exited";
			break;
		case CLD_KILLED:
			sicode = "child was killed";
			break;
		case CLD_DUMPED:
			sicode = "child terminated abnormally";
			break;
		case CLD_TRAPPED:
			sicode = "traced child has trapped";
			break;
		case CLD_STOPPED:
			sicode = "child has stopped";
			break;
		case CLD_CONTINUED:
			sicode = "stopped child has continued";
			break;
		}
		break;
	case SIGILL:
		switch (s->si_code) {
		case ILL_ILLOPC:
			sicode = "illegal opcode";
			break;
		case ILL_ILLOPN:
			sicode = "illegal operand";
			break;
		case ILL_ILLADR:
			sicode = "illegal addressing mode";
			break;
		case ILL_ILLTRP:
			sicode = "illegal trap";
			break;
		case ILL_PRVOPC:
			sicode = "privileged opcode";
			break;
		case ILL_PRVREG:
			sicode = "privileged register";
			break;
		case ILL_COPROC:
			sicode = "coprocessor error";
			break;
		case ILL_BADSTK:
			sicode = "internal stack error";
			break;
		}
		break;
	case SIGFPE:
		switch (s->si_code) {
		case FPE_INTDIV:
			sicode = "integer divide by zero";
			break;
		case FPE_INTOVF:
			sicode = "integer overflow";
			break;
		case FPE_FLTDIV:
			sicode = "floating point divide by zero";
			break;
		case FPE_FLTOVF:
			sicode = "floating point overflow";
			break;
		case FPE_FLTUND:
			sicode = "floating point underflow";
			break;
		case FPE_FLTRES:
			sicode = "floating point inexact result";
			break;
		case FPE_FLTINV:
			sicode = "invalid floating point operation";
			break;
		case FPE_FLTSUB:
			sicode = "subscript out of range";
			break;
		}
		break;
	case SIGSEGV:
		switch (s->si_code) {
		case SEGV_MAPERR:
			sicode = "address not mapped to object";
			break;
		case SEGV_ACCERR:
			sicode = "invalid permissions for mapped object";
			break;
		default:
			sicode = "segmentation fault";
			break;
		}
		break;
	}

	return sicode;
}

void sighandler(int signal, siginfo_t * s, void *ptr)
{
	ucontext_t *u = (ucontext_t *) ptr;
	unsigned int fault = 0;
	char *hfault = 0;
	char *signame = 0;
	char *sicode = 0;
	char defsicode[200];
	unsigned int r = 0;
	char *accesscolor = "";

#if defined(__i386__) || defined(__amd64__)
//#if defined(__toto__)
	/**
	* Get access type
	*/
	if (u->uc_mcontext.gregs[REG_ERR] & 0x2) {
		fault = FAULT_WRITE;	// Write fault
		hfault = "Write";
		r = 2;
		accesscolor = YELLOW;
	} else if (s->si_addr == (void*)u->uc_mcontext.gregs[REG_RIP]) {
		fault = FAULT_EXEC;	// Exec fault
		hfault = "Exec";
		r = 4;
		accesscolor = RED;
	} else {
		fault = FAULT_READ;	// Read fault
		hfault = "Read";
		r = 1;
		accesscolor = GREEN;
	}

	/**
	* Get signal name
	*/
	signame = signaltoname(signal);

	/**
	* Get signal code
	*/
	sicode = sicode_strerror(signal, s);
	if (!sicode) {
		memset(defsicode, 0x00, 200);
		snprintf(defsicode, 199, "Error code %d", s->si_code);
		sicode = defsicode;
	}

	if ((wsh->totsignals == 0) || (wsh->opt_verbose)) {
		fprintf(stderr, "\n%s[%s]\t%s\t%p" BLUE "        (%s)\n" NORMAL, accesscolor, signame, hfault, s->si_addr, sicode);

		if((fault != FAULT_EXEC)||(!msync(u->uc_mcontext.gregs[REG_RIP]&~0xfff, getpagesize(), 0))){	// Avoid segfaults on generating backtraces...
			print_backtrace();
		}

	}

	if (!wsh->totsignals) {	// Save informations relative to first signal
		wsh->firstsignal = signal;
		wsh->firstsicode = s->si_code;
		wsh->faultaddr = s->si_addr;
		wsh->reason = r;
		memcpy(wsh->errcontext, u, sizeof(ucontext_t));
		wsh->btcaller = u->uc_mcontext.gregs[REG_RIP];
	}

	if (!wsh->firsterrno) {
		wsh->firsterrno = errno;
	}			// Save first errno as firsterrno (treated separately)

	wsh->totsignals += 1;
	wsh->globalsignals += 1;

#ifdef DEBUG
	fprintf(stderr, "FATAL ERROR: Instruction Pointer 0x%012llx addr:%012llx\n", u->uc_mcontext.gregs[REG_RIP], s->si_addr);
#endif

#elif defined(__aarch64__)

	fault = FAULT_UNKNOWN;	// Unknown fault
	hfault = "Unknown";
	r = 8;
	accesscolor = BLUE;
    
	/**
	* Get signal name
	*/
	signame = signaltoname(signal);

	/**
	* Get signal code
	*/
	sicode = sicode_strerror(signal, s);
	if (!sicode) {
		memset(defsicode, 0x00, 200);
		snprintf(defsicode, 199, "Error code %d", s->si_code);
		sicode = defsicode;
	}

	if ((wsh->totsignals == 0) || (wsh->opt_verbose)) {
		fprintf(stderr, "\n%s[%s]\t%s\t%p" BLUE "        (%s)\n" NORMAL, accesscolor, signame, hfault, s->si_addr, sicode);

		if((fault != FAULT_EXEC)&&(fault != FAULT_UNKNOWN)&&(u->uc_mcontext.pc&~0xfff)&&(!msync(u->uc_mcontext.pc&~0xfff, getpagesize(), 0))){	// Avoid segfaults on generating backtraces...
			print_backtrace();
		}
	}

	if (!wsh->totsignals) {	// Save informations relative to first signal
		wsh->firstsignal = signal;
		wsh->firstsicode = s->si_code;
		wsh->faultaddr = s->si_addr;
		wsh->reason = r;
		memcpy(wsh->errcontext, u, sizeof(ucontext_t));
		wsh->btcaller = u->uc_mcontext.pc;
	}

	if (!wsh->firsterrno) {
		wsh->firsterrno = errno;
	}			// Save first errno as firsterrno (treated separately)

	wsh->totsignals += 1;
	wsh->globalsignals += 1;

#else	// Non intel, non Aarch64


	/**
	* Get access type
	*/
	fault = FAULT_UNKNOWN;	// Unknown fault
	hfault = "Unknown";
	r = 8;
	accesscolor = BLUE;

	/**
	* Get signal name
	*/
	signame = signaltoname(signal);

	/**
	* Get signal code
	*/
	sicode = sicode_strerror(signal, s);
	if (!sicode) {
		memset(defsicode, 0x00, 200);
		snprintf(defsicode, 199, "Error code %d", s->si_code);
		sicode = defsicode;
	}

	if ((wsh->totsignals == 0) || (wsh->opt_verbose)) {
		fprintf(stderr, "\n%s[%s]\t%s\t%p" BLUE "        (%s)\n" NORMAL, accesscolor, signame, hfault, s->si_addr, sicode);

	}

	if (!wsh->totsignals) {	// Save informations relative to first signal
		wsh->firstsignal = signal;
		wsh->firstsicode = s->si_code;
		wsh->faultaddr = s->si_addr;
		wsh->reason = r;
		memcpy(wsh->errcontext, u, sizeof(ucontext_t));
//		wsh->btcaller = u->uc_mcontext.gregs[REG_RIP];
	}

	if (!wsh->firsterrno) {
		wsh->firsterrno = errno;
	}			// Save first errno as firsterrno (treated separately)

	wsh->totsignals += 1;
	wsh->globalsignals += 1;

#endif // end of non Intel

	/**
	* Restore execution from known good point
	*/
	errno = ENOTRECOVERABLE; /*EFAULT;*/
	longjmp(wsh->longjmp_ptr, 1);
}

/**
* Set all signal handlers
*/
int set_sighandlers(void)
{
	struct sigaction sa;

	sa.sa_flags = SA_SIGINFO | SA_RESTART;
	sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = sighandler;

	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	if (sigaction(SIGABRT, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	if (sigaction(SIGILL, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	sa.sa_flags = SA_SIGINFO;
	sa.sa_sigaction = traphandler;
	if (sigaction(SIGTRAP, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	sa.sa_flags = SA_SIGINFO;
	sa.sa_sigaction = alarmhandler;
	if (sigaction(SIGALRM, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	sa.sa_flags = SA_SIGINFO;
	sa.sa_sigaction = inthandler;
	if (sigaction(SIGINT, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	sa.sa_sigaction = bushandler;
	sa.sa_flags = SA_SIGINFO ;
        sigfillset(&sa.sa_mask);
	if (sigaction(SIGBUS, &sa, NULL) == -1) {
		perror("sigaction");
		_Exit(EXIT_FAILURE);
	}

	return 0;
}

/**
* Set global variable is_stdinscript to 1 if there is data on stdin
*/
int test_stdin(void)
{
	struct pollfd fds;
	int ret = 0;

	fds.fd = 0;		/* fd corresponding to STDIN */
	fds.events = POLLIN;
	ret = poll(&fds, 1, 0);
	if (ret == 1) {
		wsh->is_stdinscript = 1;
		wsh->opt_hollywood = 0;
	} else if (ret == 0) {
		wsh->is_stdinscript = 0;
	} else {
		wsh->is_stdinscript = 0;
	}

	return 0;
}

int wsh_appear(lua_State * L)
{
	wsh->opt_appear = 1;
	rescan();
	parse_link_vdso();

	return 0;
}

int wsh_hide(lua_State * L)
{
	wsh->opt_appear = 0;
	rescan();

	return 0;
}

int verbose(lua_State * L)
{
	void *arg = 0;

	if (lua_isnumber(L, 1)) {
		arg = (unsigned long) lua_tonumber(L, 1);
	}

	printf(" -- Setting verbosity to %lu\n", (unsigned long)arg);
	wsh->opt_verbose = arg;

	return 0;
}

int hollywood(lua_State * L)
{
	void *arg = 0;

	if (lua_isnumber(L, 1)) {
		arg = (unsigned long) lua_tonumber(L, 1);
	}

	printf(" -- Setting hollywood to %lu\n", (unsigned long)arg);
	wsh->opt_hollywood = arg;

	if (wsh->opt_hollywood == 2) {
		printf(GREEN);
	}

	if (wsh->opt_hollywood == 1) {
		printf(NORMAL);
	}

	return 0;
}


/**
* Display mapped sections
*/
int map(lua_State * L)
{
	unsigned int count = 0;
	char *sizes[] = { "b", "Kb", "Mb", "Gb", "Tb", "Pb", "Hb" };
	double len = 0;
	int order = 0;
	struct section *s;

	s = zfirst;
	while (s != 0x00) {
		if (wsh->opt_hollywood) {
			char *pcolor = DARKGRAY;	// NORMAL

			switch (s->perms) {
			case 2:	// r--
				pcolor = GREEN;
				break;
			case 6:	// rw-
				pcolor = BLUE;
				break;
			case 3:	// r-x
				pcolor = RED;
				break;
			case 7:	// rwx
				pcolor = MAGENTA;
				break;
			default:
				break;
			}

			printf(GREEN "%012llx-%012llx" NORMAL "    %s    %s%s" NORMAL "\t\t%lu\n", s->init, s->end, s->hperms, pcolor, s->name, s->size / sysconf(_SC_PAGE_SIZE));
		} else {
			printf("%012llx-%012llx    %s    %s\t\t%lu\n", s->init, s->end, s->hperms, s->name, s->size / sysconf(_SC_PAGE_SIZE));
		}
		if (s->perms) {
			count += s->size / sysconf(_SC_PAGE_SIZE);
		}
		s = s->next;
	}

	len = count * sysconf(_SC_PAGE_SIZE);
	order = 0;
	while ((len >= 1024) && (order <= 3)) {
		order++;
		len = len / 1024;
	}

	printf(" --> total: %u pages mapped (%d %s)\n", count, (unsigned int) len, sizes[order]);

	return 0;
}

/**
* Pollute .bss sections
*/
int bsspolute(lua_State * L)
{
	sections_t *s = 0, *stmp = 0;
	char poison = 0xff;
	unsigned int num = 0;

	DL_FOREACH_SAFE(wsh->shdrs, s, stmp) {
		if((s->name)&&(!strncmp(s->name,".bss",4))){
			num++;
			if(num >= 4){
				printf("[%02u] 0x%012lx-0x%012lx    %s:%s\t%02x\t%s:%lu\t\t\n",num, s->addr, s->addr + s->size, s->name, s->perms, poison, s->libname, s->size);
				memset(s->addr, poison--, s->size);
			}
		}
		s = s->next;
	}

	return 0;
}


/**
* Search a pattern in memory
*/
static char *searchmem(char *start, char *pattern, unsigned int patternlen, unsigned int memsz)
{
	unsigned int i = 0;
	char *ptr = 0;
	unsigned int uplim = 0;

	ptr = start;
	uplim = memsz - patternlen;

	for (i = 0; (i < uplim) && (uplim > 0); i++) {
		if (!memcmp(ptr + i, pattern, patternlen)) {
			return ptr + i;
		}
	}

	return 0;
}

/**
* ralloc(unsigned int size, unsigned char poison);
* allocate 1 page set to 0x00, set size bytes to poison, remap the page R only
*/
int ralloc(lua_State * L)
{
	unsigned int size = 0;
	unsigned char poison = 0;
	unsigned long int ret = 0;
	char *ptr = 0;
//	unsigned long int *ptr2 = 0;
	unsigned int sz = 0;
	unsigned long int baseaddr = 0;

	read_arg1(size);
	read_arg2(poison);

	sz = getpagesize();

	baseaddr = (default_poison + global_xalloc)*0x1010101000; //0x81818181000-0x1000
	ptr = mmap(baseaddr, sz, PROT_WRITE|PROT_READ, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);

	if(ptr == 0){
		fprintf(stderr, "ERROR: malloc() : %s",strerror(errno));
		return 0;
	}

	ret = ptr;		// compute return address

	if(wsh->opt_verbosetrace){
		printf("-- ralloc() ptr:%p, size:%u, ret:%lx\t[%lx-%lx]\n", ptr, sz, ret, ret, ret + size);
	}

	mprotect(ptr, sz, PROT_EXEC | PROT_READ | PROT_WRITE);

	mymemset(ptr, poison ? poison : default_poison + global_xalloc, sz);	// map with default poison bytes
	global_xalloc++;

	mymemset(ptr,poison, size % 4096);

	mprotect(ptr, sz, PROT_READ);

	lua_pushlightuserdata(L, ret);

	return 1;
}

/**
* xalloc(unsigned int size,  unsigned char poison, unsigned int perms);
* Allocate size bytes (% getpagesize())
*
* The mapping auto-references itself, unless a poison byte is given
*
* [page unmaped]
* [mapped][OURPTR, size]
* [page unmaped]
*/

ATTRIBUTE_NO_SANITIZE_ADDRESS
int xalloc(lua_State * L)
{
	unsigned int size = 0;
	unsigned int perms = 0;
	unsigned char poison = 0;
	unsigned long int ret = 0;
	char *ptr = 0;
	unsigned long int *ptr2 = 0;
	unsigned int sz = 0;
	unsigned long int baseaddr = 0;

	read_arg1(size);
	read_arg2(poison);
	read_arg3(perms);

	size = size % 4096;

	sz = getpagesize()*3;

	baseaddr = (default_poison + global_xalloc)*0x1010101000-0x1000; //0x616161616000
	ptr = mmap(baseaddr, sz, PROT_WRITE|PROT_READ, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);

	if(ptr == 0){
		fprintf(stderr, "ERROR: malloc() : %s",strerror(errno));
		return 0;
	}

	ret = ptr + 2*getpagesize() - size;		// compute return address

	if(wsh->opt_verbosetrace){
		printf("-- ptr:%p, size:%u, ret:%lx\t[%lx-%lx]\n", ptr, sz, ret, ret, ret + size);
	}

	mprotect(ptr, sz, PROT_EXEC | PROT_READ | PROT_WRITE);

	mymemset(ptr, poison ? poison : default_poison + global_xalloc, sz);	// map with default poison bytes
	global_xalloc++;

	if(!poison){	// If autoref, overwrite all the content with address of our own buffer

		for(ptr2 = ptr; ptr2 < (unsigned long*)(ptr + sz) ; ptr2++){	// all 3 pages
			*ptr2 = ret;
		}

		for(ptr2 = ret; ptr2 < (unsigned long*)(ret + size) ; ptr2++){	// just our small allocade part
			*ptr2 = ret;
		}
	}

	mprotect(ptr, sz, perms ? perms : PROT_READ | PROT_WRITE | PROT_EXEC);

	mprotect(ptr+2*getpagesize(), getpagesize(), PROT_NONE);	// Third page is remapped with no permissions

	lua_pushlightuserdata(L, ret);

	return 1;
}

/**
* Release a bloc allocated via xalloc()
*/
void xfree(lua_State * L)
{
	void *ptr = 0, *trueptr = 0;
	unsigned int sz = 0;

	sz = 3*getpagesize();
	read_arg1(ptr);

	trueptr = ((unsigned long int)ptr & ~0xfff)-0x1000;
	mprotect(trueptr, sz, PROT_EXEC | PROT_READ | PROT_WRITE);
	memset(trueptr, 0x00, sz);
	munmap(trueptr, sz);
}

void traceunaligned(lua_State * L)
{
	wsh->trace_singlebranch = 0;
	wsh->trace_singlestep = 0;
	wsh->trace_unaligned = 1;
}

void untraceunaligned(lua_State * L)
{
	wsh->trace_singlebranch = 0;
	wsh->trace_singlestep = 0;
	wsh->trace_unaligned = 0;
}

void singlestep(lua_State * L)
{
	wsh->trace_singlebranch = 0;
	wsh->trace_singlestep = 1;
	wsh->trace_unaligned = 0;
}

void unsinglestep(lua_State * L)
{
	wsh->trace_singlebranch = 0;
	wsh->trace_singlestep = 0;
	wsh->trace_unaligned = 0;
}


void systrace(lua_State * L)
{
	wsh->trace_strace = 1;
	wsh->trace_rtrace = 0;
}

void rtrace(lua_State * L)
{
	wsh->trace_rtrace = 1;
	wsh->trace_strace = 0;
}

void unsystrace(lua_State * L)
{
	wsh->trace_rtrace = 0;
	wsh->trace_strace = 0;
}

void unrtrace(lua_State * L)
{
	wsh->trace_rtrace = 0;
	wsh->trace_strace = 0;
}


void verbosetrace(lua_State * L)
{
	wsh->opt_verbosetrace = 1;
}

void unverbosetrace(lua_State * L)
{
	wsh->opt_verbosetrace = 0;
}

void singlebranch(lua_State * L)
{

	//
	// Technically, it may be possible to give wsh apabilities to run BTR without uid 0
	//
	// sudo setcap cap_sys_rawio=ep ./wsh

	if(getuid() != 0){
		fprintf(stderr, "ERROR: You need root privileges to use Branch Tracing\n");
		return;
	}

	// Load LKMs in kernel land
	system("sudo modprobe cpuid");
	system("sudo modprobe msr");


	wsh->trace_singlebranch = 1;
	wsh->trace_singlestep = 0;
	wsh->trace_unaligned = 0;
}

void unsinglebranch(lua_State * L)
{
	wsh->trace_singlebranch = 0;
	wsh->trace_singlestep = 0;
	wsh->trace_unaligned = 0;
}

/**
* Search a given value in memory
*
* grepptr(Pattern, patternlen, hexadumplen, nbytesbeforematch)
*
*/
int grepptr(lua_State * L)
{
//	char *ptr = 0;
//	unsigned long int maxlen = 0, i = 0;
	char *match = 0;
	int count = 1;
	unsigned int dumplen = 200;
	unsigned int k = 0;

	unsigned long int p = 0;
	char pattern[9];
	unsigned int patternsz = 0;
	unsigned int aligned = 0;

	sections_t *s = 0, *stmp = 0;

	read_arg1(p);
	read_arg2(patternsz);
	read_arg3(aligned);

	if (!patternsz) {
		patternsz = sizeof(unsigned long int);
	}
	if (patternsz > 8) {
		fprintf(stderr, "ERROR: Wrong pattern size:%u > 8\n", patternsz);
	}

	printf(" -- Searching Pointer: 0x%lx (length:%u aligned:%u)\n", p, patternsz, aligned);
	memset(pattern, 0x00, 9);
	memcpy(pattern, &p, patternsz);

	/* create result table */
	lua_newtable(L);

	DL_FOREACH_SAFE(wsh->shdrs, s, stmp) {
		k = 0;
		if (!msync(s->addr&~0xfff, s->size, 0)) {
		      searchagain:
			match = searchmem(s->addr + k, pattern, patternsz, s->size - k);
			if (match) {
				if (wsh->opt_hollywood) {
					printf("    match[" GREEN "%d" NORMAL "] at " GREEN "%p" NORMAL " %lu bytes within:%lx-%lx:" GREEN "%s:%s" NORMAL ":%s\n\n", count, match,
					       match - (char *) s->addr, s->addr, s->addr + s->size, s->libname, s->name, s->perms);
				} else {
					printf("    match[%d] at %p %lu bytes within:%lx-%lx:%s:%s\n\n", count, match, match - (char *) s->addr, s->addr, s->addr + s->size, s->name, s->perms);
				}
				int delta = (char *) (s->addr+s->size) - match;
				if (delta > (int)dumplen) {
					delta = dumplen;
				};
				hexdump((unsigned char*)match, patternsz + delta, 0, patternsz);	// Colorize match
				printf("\n");

				/* Add symbol to Lua table */
				lua_pushnumber(L, count);		/* push key */
				lua_pushinteger(L, (unsigned long int)match);		/* push value : matching address */
			        lua_settable(L, -3);

				count++;
				k = match - s->addr + 1;
				goto searchagain;
			}
		}
//		s = s->next;
	}

	// Push number of results as lua return variable
//	lua_pushinteger(L, count);

	return 1;
}

/**
* Load a binary into the address space
*/
int loadbin(lua_State * L)
{
	char *libname = 0;

	read_arg1(libname);
	if(!libname){
		printf("ERROR: missing name of binary to load\n");
		return 0;
	}

	do_loadlib(libname);
	rescan();
	
	return 0;
}


/**
* search a pattern over all sections mapped in memory
*/
int grep(lua_State * L)
{				// Pattern, patternlen, hexadumplen, nbytesbeforematch
//	char *ptr = 0;
//	unsigned int maxlen = 0, i = 0;
	char *match = 0;
	int count = 0;
	char *pattern = 0;
	unsigned int patternlen = 0;
	unsigned int dumplen = 0;
	unsigned int nbytesbeforematch = 0;
	unsigned int k = 0;

	sections_t *s, *stmp;

	read_arg1(pattern);
	read_arg2(patternlen);
	read_arg3(dumplen);
	read_arg(nbytesbeforematch, 4);

	// Enforce sane defaults on optional arguments
	if (!patternlen) {
		patternlen = strlen(pattern);
	}
	if (!dumplen) {
		dumplen = 200;
	}

	/* create result table */
	lua_newtable(L);

	DL_FOREACH_SAFE(wsh->shdrs, s, stmp) {
		k = 0;
		if (!msync(s->addr&~0xfff, s->size, 0)) {
		      searchagain:
			match = searchmem(s->addr + k, pattern, patternlen, s->size - k);
			if (match) {
				if (wsh->opt_hollywood) {
					printf("    match[" GREEN "%d" NORMAL "] at " GREEN "%p" NORMAL " %lu bytes within:%lx-%lx:" GREEN "%s:%s" NORMAL ":%s\n\n", count + 1, match,
					       match - (char *) s->addr, s->addr, s->addr + s->size, s->libname, s->name, s->perms);
				} else {
					printf("    match[%d] at %p %lu bytes within:%lx-%lx:%s:%s\n\n", count + 1, match, match - (char *) s->addr, s->addr, s->addr + s->size, s->name, s->perms);
				}
				int delta = (char *) (s->addr+s->size) - match;
				if (delta > (int)dumplen) {
					delta = dumplen;
				};
				hexdump((unsigned char*)(match - nbytesbeforematch), patternlen + delta, nbytesbeforematch, patternlen);	// Colorize match
				printf("\n");

				/* Add symbol to Lua table */
				lua_pushnumber(L, count + 1);		/* push key */
				lua_pushinteger(L, (unsigned long int)match);		/* push value : matching address */
			        lua_settable(L, -3);

				count++;
				k = match - s->addr + 1;
				goto searchagain;
			}
		}
	}

	// Push number of results as lua return variable
//	lua_pushinteger(L, count);

	return 1;
}

/*
* Return a section from an address
*/
static struct section *sec_from_addr(unsigned long int addr)
{
	struct section *s = zfirst;

	while (s != 0x00) {
		if ((s->init <= addr) && (s->end > addr)) {
			return s;
		}
	}

	return 0;
}

/**
* Our own version of memcpy callable from LUA
*/
int priv_memcpy(lua_State * L)
{
	void *arg1 = 0, *arg2 = 0, *arg3 = 0;
	int ret = 0;

	read_arg1(arg1);
	read_arg2(arg2);
	read_arg3(arg3);

	ret = memcpy(arg1, arg2, arg3);

	// Push number of results as lua return variable
	lua_pushinteger(L, ret);

	return 1;
}

/**
* Our own version of strcpy callable from LUA
*/
int priv_strcpy(lua_State * L)
{
	void *arg1 = 0, *arg2 = 0;
	int ret = 0;

	read_arg1(arg1);
	read_arg2(arg2);

	ret = strcpy(arg1, arg2);

	// Push number of results as lua return variable
	lua_pushinteger(L, ret);

	return 1;
}

/**
* Our own version of strcat callable from LUA
*/
int priv_strcat(lua_State * L)
{
	void *arg1 = 0, *arg2 = 0;
//	char *ptr = 0;
//	char *addr = 0;
	int ret = 0;

	read_arg1(arg1);
	read_arg2(arg2);

	ret = strcat(arg1, arg2);

	// Push number of results as lua return variable
	lua_pushinteger(L, ret);

	return 1;
}

/**
* Set a breakpoint
*/
int breakpoint(lua_State * L)
{
	void *arg1 = 0, *arg2 = 0;
	char *ptr = 0;
	char *addr = 0;
	char bk = 0;

	read_arg1(arg1);
	read_arg1(arg2);

	/**
	* Make sure destination address is mapped
	*/
	if ((!arg1) || (msync((long int)arg1 & (long int)~0xfff, 4096, 0))) {
		fprintf(stderr, "ERROR: Address %p is not mapped\n", arg1);
		return 0;
	}

	/**
	* Change memory protections to RWX on destionation's page
	*/
	ptr = arg1;
	addr = ((unsigned long int) ptr & (unsigned long int) ~0xfff);
	printf(" ** Setting  BREAKPOINT[%u]  (weigth:%lu)	<", wsh->bp_num + 1, (unsigned long int) arg2);
	info_function(arg1);
	mprotect(addr, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC);

	/**
	* Backup byte at destination
	*/
	bk = ptr[0x00];

	/**
	* Write Breakpoint
	*/
	ptr[0x00] = 0xcc;

	/**
	* Save breakpoint informations
	*/
	if (!wsh->bp_num) {
		wsh->bp_array = calloc(1, sizeof(breakpoint_t));
	} else {
		wsh->bp_array = realloc(wsh->bp_array, sizeof(breakpoint_t) * (wsh->bp_num + 1));
	}

	wsh->bp_array[wsh->bp_num].ptr = ptr;
	wsh->bp_array[wsh->bp_num].backup = bk;
	wsh->bp_array[wsh->bp_num].weight = arg2;
	wsh->bp_num++;

	return 0;
}

void declare_func(void *addr, char *name)
{
	lua_pushcfunction(wsh->L, addr);
	lua_setglobal(wsh->L, name);
}

void declare_num(int val, char *name)
{
	lua_pushnumber(wsh->L, val);
	lua_setglobal(wsh->L, name);
}

/**
* Export functions to lua
*/
void declare_internals(void)
{
	unsigned int i;

	/**
	* Create definitions for internal functions
	*/
	for(i=0;i<sizeof(exposed)/sizeof(tuple_t);i++){
		declare_func(exposed[i].addr, exposed[i].name);	// Declare function within Lua
	}

	declare_num(wsh->bp_points, "bp_points");

	/**
	* Create a wrapper functions for other internal functions
	*/
	char *luacmd = calloc(1, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", "hexdump", "lhexdump");
	luabuff_append(luacmd);
	memset(luacmd, 0x00, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, string.len(a), c, d, e, f, g, h); return j, k; end\n", "hex", "lhexdump");
	luabuff_append(luacmd);
	memset(luacmd, 0x00, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", "execlib", "lexeclib");
	luabuff_append(luacmd);
	memset(luacmd, 0x00, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", "disasm", "ldisasm");
	luabuff_append(luacmd);
	memset(luacmd, 0x00, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", "deref", "lderef");
	luabuff_append(luacmd);
	memset(luacmd, 0x00, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", "strace", "lstrace");
	luabuff_append(luacmd);
	memset(luacmd, 0x00, 1024);
	snprintf(luacmd, 1023, "function %s (a, b, c, d, e, f, g, h) j,k = libcall(%s, a, b, c, d, e, f, g, h); return j, k; end\n", "script", "lscript");	
	luabuff_append(luacmd);
	free(luacmd);
}


int set_alloc_opt(void)
{
	setenv("LIBC_FATAL_STDERR_", "yes", 1);
#ifdef __GLIBC__
	mallopt(M_CHECK_ACTION, 3);
#endif
	return 0;
}

/**
* Generate a core file
*/
int gencore(lua_State * L)
{
	enable_core(L);
	if(!fork()){
		kill(getpid(), SIGQUIT);
	}

	return 0;
}

/**
* Disable core files generation
*/
int disable_core(lua_State * L)
{

	int err = 0;

	errno = 0;
	err = prctl(PR_SET_DUMPABLE, (long)0);

	if(err){
		printf("ERROR: prctl() %s\n", strerror(errno));
	}

	return 0;
}

/**
* Enable core files generation
*/
int enable_core(lua_State * L)
{
	int err = 0;
	errno = 0;

	err = prctl(PR_SET_DUMPABLE, (long)1);
	if(err){
		printf("ERROR: prctl() %s\n", strerror(errno));
	}

	return 0;
}

int wsh_init(void)
{
	int status = 0;

	set_sighandlers();

	// create context
	wsh = calloc(1, sizeof(wsh_t));

	// test if we're reading a script from stdin
	test_stdin();

	// create lua state
	wsh->L = luaL_newstate();	/* Create Lua state variable */

	// make sure version of lua matches
	luaL_checkversion(wsh->L);

	luaL_openlibs(wsh->L);	/* Load Lua libraries */

	// Declare internal functions
	declare_internals();

	// Load indirect functions
	load_indirect_functions(wsh->L);

	// Default is to disable core files
//	disable_core(wsh->L);

	// Set malloc options
	set_alloc_opt();

	// Set process name
	prctl(PR_SET_NAME,"wsh",NULL,NULL,NULL);

	// Register disassembler functions
	init_multiarch_support();

	// Initialize lua2c()
	init_lua2c(wsh->L);

	// Initialize struct2c()
	init_struct2c(wsh->L);

	// Load json.lua by default
	status = luaL_dostring(wsh->L, "json = dofile('/usr/share/wcc/scripts/json.lua')");
	if (status != LUA_OK) {
		fprintf(stderr, "Warning: Could not load JSON library: %s\n", lua_tostring(wsh->L, -1));
		lua_pop(wsh->L, 1);  // Remove error message
		fprintf(stderr, "JSON-defined structures will not be readily available.\n");
		fprintf(stderr, "To enable: ensure /usr/share/wcc/scripts/json.lua exists and is readable\n");
	} else {
		if (wsh->opt_verbose) {
			printf("JSON library loaded - struct2c() available!\n");
		}
	}

	return 0;
}

static char *lua_strerror(int err)
{
	switch (err) {
	case 1:
		return "Lua Yield";
	case 2:
		return "Runtime Error";
	case 3:
		return "Synthax Error";
	case 4:
		return "Memory Error";
	case 5:
		return "Fatal Error";
	default:
		return "Unknown Error";
	};

	return NULL;		// Never reached
}

/**
* Run a lua script
*/
int run_script(char *name)
{
	char myerror[200];
	int err = 0;

	if(!name){ return -1;}

	if(wsh->opt_verbose){
		printf("  * Running lua script %s\n", name);
	}

	memset(myerror, 0x00, 200);
	err = 0;
	if ((err = luaL_loadfile(wsh->L, name) != 0)) {	/* Load but don't run the Lua script */
		snprintf(myerror, 199, "error %d : %s", err, lua_strerror(err));
		printf(stderr, "luaL_loadfile() failed for script %s (%s)\n", name, errno ? strerror(errno) : myerror);	/* Error out if file can't be read */
		return -1;
	}
	if (wsh->opt_verbose) {
		printf("  * Running lua script %s\n", name);
	}

	memset(myerror, 0x00, 200);

	if (lua_pcall(wsh->L, 0, 0, 0)) {	/* Run the loaded Lua script */
		fprintf(stderr, "WARNING: lua_pcall() failed with %s, for: %s\n",lua_tostring(wsh->L, -1), name);	/* Error out if Lua file has an error */
		lua_pop(wsh->L, 1);	// pop error message from the stack 
	}
	lua_settop(wsh->L, 0);	// remove eventual returns 

	return 0;
}

/**
* Verify ELF signature in a binary
*/
unsigned int read_elf_sig(char *fname, struct stat *sb)
{
	int fd = 0;
	char sig[5];
	char validelf[4] = "\177ELF";
	if (sb->st_size < MIN_BIN_SIZE) {
		return 0;	// Failure
	}
	fd = open(fname, O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 0;
	}
	memset(sig, 0x00, 5);
	read(fd, sig, 4);
	close(fd);

	return strncmp(sig, validelf, 4) ? 0 : 1;
}

/**
* Verify ar archive signature in a binary
*/
unsigned int read_archive_sig(char *fname, struct stat *sb)
{
	int fd = 0;
	char sig[10];
//	char validelf[4] = "\177ELF";
	if (sb->st_size < 20) {
		return 0;	// Failure
	}
	fd = open(fname, O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 0;
	}
	memset(sig, 0x00, 10);
	read(fd, sig, 10);
	close(fd);

	return strncmp(sig, "!<arch>", 7) ? 0 : 1;
}


/**
* Execute default internal scripts
*/
int exec_default_scripts(void)
{
	int err = 0;

	if ((err =luaL_loadfile(wsh->L, DEFAULT_SCRIPT_INDEX)) != 0) {
		printf("ERROR: %s in script %s (%s)\n", lua_strerror(err), DEFAULT_SCRIPT_INDEX, lua_tostring(wsh->L, -1));
		fatal_error(wsh->L, "luaL_loadfile() failed");
	}
	if(lua_pcall(wsh->L, 0, 0, 0)){
		fprintf(stderr, "WARNING: lua_pcall() failed with %s\n",lua_tostring(wsh->L, -1));
	}

	return 0;
}


int load_home_user_file(char *fname)
{
	char pathname[255];
	struct stat sb;
	int err = 0;

	memset(pathname, 0x00, 255);

	if(!getenv("HOME")){
		printf("WARNING: HOME environment variable not set : skipping %s file", fname);
		return -1;
	}

	snprintf(pathname, 254, "%s/%s", getenv("HOME"), fname);

	if (stat(pathname, &sb) == -1) {
		if(wsh->opt_verbose){
			printf("WARNING: %s file not found\n", pathname);
		}
		errno = 0;
		return -1;
	}

	if(wsh->opt_verbose){
		printf("  * Running user startup script %s\n", pathname);
	}

	// load file from home user directory if present
	err = 0;
	if ((err = luaL_loadfile(wsh->L, pathname)) != 0) {
		printf("WARNING: %s while running startup script %s (%s)\n", lua_strerror(err), pathname, lua_tostring(wsh->L, -1));
	}

	if(lua_pcall(wsh->L, 0, 0, 0)){
		fprintf(stderr, "WARNING: lua_pcall() failed with %s\n",lua_tostring(wsh->L, -1));
	}

	return 0;
}


/**
* Load .wsh_profile script if it exists
* We search for it in the directory
* corresponding to environment variable HOME
*/
int load_profile(void)
{
	load_home_user_file(DEFAULT_WSH_PROFILE);
	return 0;
}

/**
* Load .wshrc script if it exists
* We search for it in the directory
* corresponding to environment variable HOME
*/
int load_wshrc(void)
{
	load_home_user_file(DEFAULT_WSHRC);
	return 0;
}

/**
* Run a lua shell/script
*/
int wsh_run(void)
{
	struct script_t *s = 0;
	unsigned int scriptcount = 0;

	DL_COUNT(wsh->scripts, s, scriptcount);

	read_maps(getpid());
	parse_link_map_dyn((struct link_map *)wsh->mainhandle);
	parse_link_vdso();

	/**
	* run internal lua buffers
	*/
	exec_luabuff();

	if (wsh->opt_verbose) {
		printf(" -- Running startup lua scripts\n");
	}

	/**
	* Execute default internal scripts
	*/
	exec_default_scripts();

	/**
	* load .wshrc if present
	*/
	load_wshrc();

	/**
	* Run all the scripts specified in the command line
	*/
	if (wsh->opt_verbose) {
		printf(" -- %u user scripts in queue\n", scriptcount);
	}

	script_t *ss, *stmp;
	DL_FOREACH_SAFE(wsh->scripts, ss, stmp) {
		run_script(ss->name);
	}

	/**
	* Run a lua shell
	*/
	if (!sigsetjmp(wsh->longjmp_ptr_high, 1)){ // This is executed only the first time // save stack context + signals
		run_shell(wsh->L);
	}else{
		if(wsh->longjmp_ptr_high_cnt++ < 3){
			printf("RESTORING FROM SANE STACK STATE (%u/3)\n", wsh->longjmp_ptr_high_cnt);
			run_shell(wsh->L);
		}else{
			printf("\n%s[FATAL]\tInterrupted too many times : exiting%s\n",RED,NORMAL);
			_Exit(EXIT_FAILURE);
		}
	}

	lua_close(wsh->L);	/* Clean up, free the Lua state var */
	return 0;

}


int add_script_arguments(int argc, char **argv, unsigned int i)
{
	unsigned int j = 0;

	if (i >= (unsigned int)argc) {
		return -1;
	}			// Should not happen

	wsh->script_argnum = argc - i;

	wsh->script_args = calloc(sizeof(void *), argc);

	for (j = 0; j < wsh->script_argnum; j++) {
		wsh->script_args[j] = strdup(argv[j + i]);
		if(wsh->opt_verbose){
			printf("argument[%u]: %s\n", j, wsh->script_args[j]);
		}
	}
	return wsh->script_argnum;
}

/**
* Add a script to the execution queue
*/
int add_script_exec(char *name)
{
	struct script_t *s;

	s = calloc(1, sizeof(struct script_t));
	s->name = strdup(name);
	DL_APPEND(wsh->scripts, s);

	return 0;
}

/**
* Add a binary to the list of binaries to preload
*/
int add_binary_preload(char *name)
{
	struct preload_t *p;

	p = calloc(1, sizeof(struct preload_t));
	p->name = strdup(name);
	DL_APPEND(wsh->preload, p);

	return 0;
}

/**
* Turn a static library into a dynamic library
*/
char *ar2lib(char *name)
{
	char cmd[1024];
	char *tmp_dirname = 0;

	/**
	* Create temporary directory under /tmp/
	*
	* NOTE: The directory name written to is predictable.
	*/
	tmp_dirname = calloc(20, 1);
	snprintf(tmp_dirname, 19, "/tmp/.wsh-%u", getpid());
	if(mkdir(tmp_dirname, 0700)){
		fprintf(stderr, "ERROR : mkdir(%s, ...) %s\n", tmp_dirname, strerror(errno));
		return NULL;
	}

	// Recompile static library as dynamic library
	memset(cmd, 0x00, 1024);
	snprintf(cmd, 1023, "gcc -Wl,--whole-archive %s -Wl,--no-whole-archive  -o /tmp/.wsh-%u/%s.so -shared -rdynamic -ggdb -g3", name, getpid(), basename(name));
	system(cmd);

	memset(cmd, 0x00, 1024);
	snprintf(cmd, 1023, "/tmp/.wsh-%u/%s.so", getpid(), basename(name));

	if(verbose){
		printf(" -- relinked static library (ar archive) %s into dynamic shared library %s\n", name, cmd);	
	}
	return strdup(cmd);
}

/**
* Attempt to patch a ET_EXEC binary as ET_DYN,
* making it suitable for use with dlopen()
*/
int attempt_to_patch(char *libname)
{
	struct stat sb;
	int fdin = 0, fdout = 0;
	unsigned int copied = 0;
	char *tmp_dirname = 0;
	char *outlib = 0;

	/**
	* Verify file exists
	*/
	if (stat(libname, &sb) == -1) {
		if(wsh->opt_verbose){
			printf("WARNING: %s file not found\n", libname);
		}
		return 0; // Fail
	}

	fdin = open(libname, O_RDONLY, 0700);
	if (fdin < 0) {
		fprintf(stderr, "ERROR : open(%s, O_RDONLY) %s\n", libname, strerror(errno));
		return 0; // Fail
	}

	/**
	* Create temporary directory under /tmp/
	*
	* NOTE: The directory name written to is predictable. Bug ?
	*/
	tmp_dirname = calloc(20, 1);
	snprintf(tmp_dirname, 19, "/tmp/.wsh-%u", getpid());
	if(mkdir(tmp_dirname, 0700)){
		fprintf(stderr, "ERROR : mkdir(%s, ...) %s\n", tmp_dirname, strerror(errno));
	}

	/**
	* Open destination file
	*/

	outlib = calloc(1, 300);
	snprintf(outlib, 299, "/%s/%s", tmp_dirname, basename(libname));

	printf(" ** libifying %s to %s (%lu bytes)\n", libname, outlib, sb.st_size);

	fdout = open(outlib, O_RDWR|O_CREAT|O_TRUNC, 0700);
	if (fdout < 0) {
		fprintf(stderr, "ERROR : open(%s, O_RDWR) %s\n", outlib, strerror(errno));
		return 0; // Fail
	}

	/**
	* Copy binary under newly created directory, with Zero copy data (sendfile())
	*/
	copied = sendfile(fdout, fdin, 0, sb.st_size);
	if(copied != sb.st_size){
		fprintf(stderr, "ERROR: sendfile(); Copy failed: %s\n", strerror(errno));
		return 0; // Fail
	}

	close(fdin);
	close(fdout);

	/**
	* Patch ET_EXEC into ET_DYN
	*/
	mk_lib(outlib, 1, 1, 1, 0);

	if(wsh->libified++ != 0){
		printf("\n\n Libifying more than one (ET_EXEC) ELF per process may lead to crashes...\n\n");
	}

	return dlopen(outlib, wsh->opt_global ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW);
}


/**
* Do load a shared binary into the address space
*/
struct link_map *do_loadlib(char *libname)
{
	struct link_map *handle = 0;

	if((!libname)||(!strlen(libname))){
		printf("ERROR: missing name of binary to load\n");
		return 0;
	}

	if (wsh->opt_verbose) {
		printf("  * Loading %s\n", libname);
	}

	handle = dlopen(libname, wsh->opt_global ? RTLD_NOW | RTLD_GLOBAL : RTLD_NOW);
	if (!handle) {
		fprintf(stderr, "WARNING: dlopen() %s \n", dlerror());

		// attempt to patch binary as ET_DYN if was ET_EXEC
		if (wsh->opt_userland_load) {
			handle = 0;		
		}else{
			handle = attempt_to_patch(libname);
		}
		if (!handle) {
			if (!wsh->opt_userland_load) {
				fprintf(stderr, "ERROR: dlopen() of patched file! %s \n", dlerror());
			}
			userland_load_binary(libname);
			wsh->userland_loaded = 1;
			return 0;
		}else{
			printf(" ** loading of libified binary succeeded\n");
		}
	}

	if ((wsh->opt_verbose)&&(handle)) {
		printf("  * Base address: %p\n", (void *) handle->l_addr);
	}

	dlerror();			// Clear any existing load error
	wsh->mainhandle = handle;	// Last loaded object is always the new handle
	return handle;
}

/**
* Load all preload libraries
*/
int wsh_loadlibs(void)
{
	struct preload_t *p = 0, *tmp = 0;
	unsigned int count = 0;

	DL_COUNT(wsh->preload, p, count);

	if (wsh->opt_verbose) {
		printf(" -- Preloading %u binaries\n", count);
	}

	DL_FOREACH_SAFE(wsh->preload, p, tmp) {
		do_loadlib(p->name);
	}

	return 0;
}

/**
* Parse command line
*/
int wsh_getopt(int argc, char **argv)
{
	const char *short_opt = "hqvVxgu";
	int count = 0;
	struct stat sb;
	int c = 0, i = 0;

	struct option long_opt[] = {
		{"help", no_argument, NULL, 'h'},
		{"args", no_argument, NULL, 'x'},
		{"quiet", no_argument, NULL, 'q'},
		{"global", no_argument, NULL, 'g'},
		{"verbose", no_argument, NULL, 'v'},
		{"version", no_argument, NULL, 'V'},
		{"version", no_argument, NULL, 'u'},
		{NULL, 0, NULL, 0}
	};

	wsh->opt_hollywood = 1;	// Set sane default

	wsh->selflib = realpath("/proc/self/exe", 0);

	while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
		count++;
		switch (c) {
		case -1:	/* no more arguments */
		case 0:
			break;

		case 'h':
			wsh_usage(argv[0]);
			_Exit(EXIT_SUCCESS);
			break;

		case 'g':
			wsh->opt_global = 1;
			break;

		case 'q':
			wsh->opt_quiet = 1;
			break;

		case 'v':
			wsh->opt_verbose = 1;
			break;

		case 'V':
			wsh_print_version();
			_Exit(EXIT_SUCCESS);
			break;

		case 'u':
			wsh->opt_userland_load = 1;
			break;

		case 'x':
			goto nomoreargs;
			break;

		default:
			fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c);
			fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
			_Exit(EXIT_FAILURE);
		};
	};

nomoreargs:

	if (count >= argc - 1) {
		return 0;	// no file argument
	}

	if (wsh->opt_verbose) {
		printf(" -- Parsing command line\n");
	}

	for (i = count + 1; i < argc; i++) {

		if (!strncmp(argv[i], "-x", strlen(argv[i]))) {	// Is this an argument stopper ?
			// Every remaining argument is a script argument
			add_script_arguments(argc, argv, i + 1);
			break;
		} else if (!stat(argv[i], &sb)) {	// file exists. Determine if this is a binary or a script
			if (read_elf_sig(argv[i], &sb)) {		// Process as ELF file
				if (wsh->opt_verbose) {
					printf("  * User binary %s\n", argv[i]);
				}
				add_binary_preload(argv[i]);
			}else if (read_archive_sig(argv[i], &sb)) {	// Process as ar archive
				if (wsh->opt_verbose) {
					printf("  * ar archive %s\n", argv[i]);
				}
				add_binary_preload(ar2lib(argv[i]));
			} else {					// Process as a script
				if (wsh->opt_verbose) {
					printf("  * User script %s\n", argv[i]);
				}
				add_script_exec(argv[i]);
			}
		} else {	// Every remaining argument is a script argument
			add_script_arguments(argc, argv, i);
			break;
		}
	}

	// Load user profile
	load_profile();

	return 0;
}

/**
* Print software version
*/
int wsh_print_version(void)
{
	printf("%s version:%s    (%s %s)\n", WNAME, WVERSION, WTIME, WDATE);
	return 0;
}

/**
* Print usage
*/
int wsh_usage(char *name)
{
	printf("Usage: %s [script] [-h|-q|-v|-V|-g] [binary1] [binary2] ... [-x [script_arg1] [script_arg2] ...]\n", name);
	printf("\n");
	printf("Options:\n\n");
	printf("    -x, --args                Optional script argument separator\n");
	printf("    -q, --quiet               Display less output\n");
	printf("    -v, --verbose             Display more output\n");
	printf("    -g, --global              Bind symbols globally\n");
	printf("    -u, --userland-load       Force use userland loader\n");	
	printf("    -V, --version             Display version and build, then exit\n");
	printf("\n");
	printf("Script:\n\n");
	printf("    If the first argument is an existing file which is not a known binary file format,\n");
	printf("    it is assumed to be a lua script and gets executed.\n");
	printf("\n");
	printf("Binaries:\n\n");
	printf("    Any binary file name before the -x tag gets loaded before running the script.\n");
	printf("    The last binary loaded is the main binary analyzed.\n");
	printf("\n");
	return 0;
}


int teletype(lua_State * L)
{
	char *str = 0;
	unsigned int i = 0;

	read_arg1(str);
	printf("%s", GREEN);
	fflush(stdout);
	for(i = 0; i < strlen(str); i++){
		printf("%c", str[i] & 0xff);
		usleep(10000);
		fflush(stdout);
	}
	printf("%s", NORMAL);
	printf("\n");
	fflush(stdout);
	return 0;
}


/***** Mapping of buffers *****/


/**
* string res rawmemread(addr, len)
*
* Read len bytes at address addr and return them as a lua string.
*/
int rawmemread (lua_State *L) {
	char *addr = 0;
	size_t len = 0;
	read_arg1(addr);
	read_arg2(len);

	if(addr == NULL){ return 0; }
	if((unsigned long int)addr < 4096){ return 0; }	// 1st page detection

	lua_pushlstring(L, addr, len);
	return 1;
}

/**
* int written rawmemwrite(addr, data, len)
*
* Raw write to addr of len bytes of data
* returns number of bytes written.
*/
int rawmemwrite (lua_State *L) {
	char *addr = 0;
	char *data = 0;
	size_t len = 0;
	read_arg1(addr);
	read_arg2(data);
	read_arg3(len);

	if(addr == NULL){ return 0; }
	if((unsigned long int)addr < 4096){ printf("ERROR: Write to first page forbidden\n"); return 0; }	// 1st page detection

	memmove(addr, data, len);
	lua_pushinteger(L, len);
	return 1;
}

/**
* Returns a string, from an address passed as argument.
*/
int rawmemstr (lua_State *L) {
	char *addr = 0;
	read_arg1(addr);

	if(addr == NULL){ return 0; }
	if((unsigned long int)addr < 4096){ printf("ERROR: Read first page forbidden\n"); return 0; }	// 1st page detection

	lua_pushstring(L, addr);
	return 1;
}

/**
* Display memory usage.
*/
int rawmemusage (lua_State *L) {
	struct rusage usage;
	getrusage(RUSAGE_SELF,&usage);

	printf("  %s, %li\n",  " maximum resident set size "    , usage.ru_maxrss  );
	printf("  %s, %li\n",  " page reclaims "                , usage.ru_minflt  );
	printf("  %s, %li\n",  " block input operations "       , usage.ru_inblock );
	printf("  %s, %li\n",  " block output operations "      , usage.ru_oublock );
	printf("  %s, %li\n",  " voluntary context switches "   , usage.ru_nvcsw   );
	printf("  %s, %li\n",  " involuntary context switches " , usage.ru_nivcsw  );

	printf("Memory usage: %ld Kb\n",usage.ru_maxrss);
	lua_pushinteger(L, usage.ru_maxrss);
	return 1;
}

/**
* int addr rawmemaddr(obj)
*
* Return the address in memory of the object passed as argument.
* Or returns an address itself if an address is given as argument.
*/
int rawmemaddr(lua_State *L) {
	unsigned long int len = 0;
	read_arg1(len);

	lua_pushinteger(L, len);
	return 1;
}

/**
* int rawmemstrlen(addr)
* Returns the length of a string passed as argument
*/
int rawmemstrlen(lua_State *L) {
	char *addr = 0;
	read_arg1(addr);

	lua_pushinteger(L, strlen(addr));
	return 1;
}

/**
* int wrptr(addr, data, len)
*
* returns number of bytes written.
*/

int wrptr (lua_State *L) {
	char *data = 0;
	size_t offset = 0;
	char *ptr = 0;
	read_arg1(data);
	read_arg2(offset);
	read_arg3(ptr);

	if(data == NULL){ return 0; }
	if((unsigned long int)data < 4096){ printf("ERROR: Write to first page forbidden\n"); return 0; }	// 1st page detection

	data[offset] = ptr;
	lua_pushinteger(L, data);
	return 1;
}

/**
* Set default environment variables in constructor
*/

__attribute__((constructor))
static void initialize_wsh() {
//	printf("init\n");
	setenv("LIBC_FATAL_STDERR_", "1", 1);
	setenv("MALLOC_CHECK_", "3", 1);
}

/**
* Get a pointer to a variable
*/
int getptr(lua_State * L)
{
	char *vname = 0;

	read_arg1(vname);
	if(!vname){
		printf("ERROR: missing name of variable\n");
		return 0;
	}

	lua_pushlightuserdata(L, vname);

	return 1;
}

/**
* Create a pointer to a variable
*/
int mkptr(lua_State * L)
{
	char *vname = 0;
	char **newptr = 0;

	read_arg1(vname);
	if(!vname){
		printf("ERROR: missing name of variable\n");
		return 0;
	}

	newptr = calloc(1, sizeof(char*));
	newptr[0] = vname;

	lua_pushinteger(L, newptr);

	return 1;
}


/**
* lua2c() - Convert Lua arrays to C arrays
*/


/**
* Garbage collection - cleanup when Lua GC runs
*/
static int carray_gc(lua_State *L)
{
	carray_t *carr = (carray_t *) luaL_checkudata(L, 1, CARRAY_META);
	if (carr->data) {
		if (carr->type == CARRAY_CHARPTR) {
			// Free individual strings first
			char **strings = (char **) carr->data;
			for (size_t i = 0; i < carr->length; i++) {
				if (strings[i]) {
					free(strings[i]);
				}
			}
		}
		free(carr->data);
		carr->data = NULL;
	}
	return 0;
}

/**
* Array indexing and method dispatch: carray[index] or carray.method
*/
static int carray_index(lua_State *L)
{
	carray_t *carr = (carray_t *) luaL_checkudata(L, 1, CARRAY_META);

	lua_Integer index = 0;
	int is_numeric = 0;

	// Try to get numeric index (handles both numbers and numeric strings)
	if (lua_isinteger(L, 2)) {
		index = lua_tointeger(L, 2);
		is_numeric = 1;
	} else if (lua_isnumber(L, 2)) {
		index = lua_tointeger(L, 2);
		is_numeric = 1;
	} else if (lua_isstring(L, 2)) {
		const char *key = lua_tostring(L, 2);

		// Try to convert string to number
		char *endptr;
		long num = strtol(key, &endptr, 10);
		if (*endptr == '\0' && endptr != key) {
			// Successfully converted string to number
			index = num;
			is_numeric = 1;
		} else {
			// Not a number, treat as method name
			if (strcmp(key, "ptr") == 0) {
				lua_pushcfunction(L, carray_ptr);
				return 1;
			} else if (strcmp(key, "debug") == 0) {
				lua_pushcfunction(L, print_array);
				return 1;
			}
			lua_pushnil(L);
			return 1;
		}
	}

	if (!is_numeric) {
		lua_pushnil(L);
		return 1;
	}
	// Use 0-based indexing (C-style) - no conversion needed
	if (index < 0 || (size_t) index >= carr->length) {
		lua_pushnil(L);
		return 1;
	}

	switch (carr->type) {
	case CARRAY_CHARPTR:{
			char **strings = (char **) carr->data;
			if (strings[index]) {
				lua_pushstring(L, strings[index]);
			} else {
				lua_pushnil(L);
			}
			break;
		}
	case CARRAY_INT:{
			int *ints = (int *) carr->data;
			lua_pushinteger(L, ints[index]);
			break;
		}
	case CARRAY_LONG:{
			long *longs = (long *) carr->data;
			lua_pushinteger(L, longs[index]);
			break;
		}
	case CARRAY_VOIDPTR:{
			void **ptrs = (void **) carr->data;
			lua_pushinteger(L, (lua_Integer) (uintptr_t) ptrs[index]);
			break;
		}
	}
	return 1;
}

/**
* Array assignment: carray[index] = value
*/
static int carray_newindex(lua_State *L)
{
	carray_t *carr = (carray_t *) luaL_checkudata(L, 1, CARRAY_META);
	lua_Integer index = luaL_checkinteger(L, 2);

	// Use 0-based indexing (C-style) - no conversion needed
	if (index < 0 || (size_t) index >= carr->length) {
		return luaL_error(L, "array index out of bounds");
	}

	switch (carr->type) {
	case CARRAY_CHARPTR:{
			char **strings = (char **) carr->data;
			// Free existing string
			if (strings[index]) {
				free(strings[index]);
				strings[index] = NULL;
			}
			// Set new string
			if (lua_isstring(L, 3)) {
				const char *str = lua_tostring(L, 3);
				strings[index] = strdup(str);
			} else if (lua_isinteger(L, 3)) {
				// Allow setting raw pointer
				strings[index] = (char *) (uintptr_t) lua_tointeger(L, 3);
			}
			break;
		}
	case CARRAY_INT:{
			int *ints = (int *) carr->data;
			ints[index] = (int) luaL_checkinteger(L, 3);
			break;
		}
	case CARRAY_LONG:{
			long *longs = (long *) carr->data;
			longs[index] = (long) luaL_checkinteger(L, 3);
			break;
		}
	case CARRAY_VOIDPTR:{
			void **ptrs = (void **) carr->data;
			ptrs[index] = (void *) (uintptr_t) luaL_checkinteger(L, 3);
			break;
		}
	}
	return 0;
}

/**
* Get raw C pointer for FFI usage
*/
static int carray_ptr(lua_State *L)
{
	carray_t *carr = (carray_t *) luaL_checkudata(L, 1, CARRAY_META);
	lua_pushinteger(L, (lua_Integer) (uintptr_t) carr->data);
	return 1;
}

/**
* Standalone function to print/debug any carray
*/
static int print_array(lua_State *L)
{
	// Check if argument is a carray userdata
	carray_t *carr = (carray_t *) luaL_testudata(L, 1, CARRAY_META);
	if (!carr) {
		return luaL_argerror(L, 1, "expected carray (created with lua2c)");
	}
	// Determine type name for display
	const char *type_name;
	switch (carr->type) {
	case CARRAY_CHARPTR:
		type_name = "char*";
		break;
	case CARRAY_INT:
		type_name = "int";
		break;
	case CARRAY_LONG:
		type_name = "long";
		break;
	case CARRAY_VOIDPTR:
		type_name = "void*";
		break;
	default:
		type_name = "unknown";
		break;
	}

	printf("Array (%s[%zu]) at %p:\n", type_name, carr->length, carr->data);

	for (size_t i = 0; i < carr->length; i++) {
		printf("  [%zu] = ", i);

		switch (carr->type) {
		case CARRAY_CHARPTR:{
				char **strings = (char **) carr->data;
				if (strings[i]) {
					if ((uintptr_t) strings[i] < 0x10000) {
						// Looks like a small number, probably a raw pointer value
						printf("(ptr) %p", (void *) strings[i]);
					} else {
						// Check if it's a readable string
						printf("\"%s\" (at %p)", strings[i], (void *) strings[i]);
					}
				} else {
					printf("NULL");
				}
				break;
			}
		case CARRAY_INT:{
				int *ints = (int *) carr->data;
				printf("%d", ints[i]);
				break;
			}
		case CARRAY_LONG:{
				long *longs = (long *) carr->data;
				printf("%ld", longs[i]);
				break;
			}
		case CARRAY_VOIDPTR:{
				void **ptrs = (void **) carr->data;
				printf("%p", ptrs[i]);
				break;
			}
		}
		printf("\n");
	}

	return 0;
}

/**
* Get array length
*/
static int carray_len(lua_State *L)
{
	carray_t *carr = (carray_t *) luaL_checkudata(L, 1, CARRAY_META);
	lua_pushinteger(L, carr->length);
	return 1;
}

/**
* Main conversion function: lua2c(table, type)
*/
static int lua2c(lua_State *L)
{
	// Check arguments
	luaL_checktype(L, 1, LUA_TTABLE);
	const char *type_str = luaL_checkstring(L, 2);

	// Determine target type
	carray_type_t type;
	size_t element_size;

	if (strcmp(type_str, "char*") == 0) {
		type = CARRAY_CHARPTR;
		element_size = sizeof(char *);
	} else if (strcmp(type_str, "int") == 0) {
		type = CARRAY_INT;
		element_size = sizeof(int);
	} else if (strcmp(type_str, "long") == 0) {
		type = CARRAY_LONG;
		element_size = sizeof(long);
	} else if (strcmp(type_str, "void*") == 0) {
		type = CARRAY_VOIDPTR;
		element_size = sizeof(void *);
	} else {
		return luaL_error(L, "unsupported type: %s", type_str);
	}

	// Get table length
	size_t length = lua_rawlen(L, 1);
	if (length == 0) {
		return luaL_error(L, "empty table");
	}
	// Create userdata
	carray_t *carr = (carray_t *) lua_newuserdata(L, sizeof(carray_t));
	carr->data = calloc(length, element_size);
	carr->length = length;
	carr->type = type;

	if (!carr->data) {
		return luaL_error(L, "memory allocation failed");
	}
	// Convert elements based on type
	for (size_t i = 0; i < length; i++) {
		lua_rawgeti(L, 1, i + 1);	// Lua arrays are 1-indexed

		switch (type) {
		case CARRAY_CHARPTR:{
				char **strings = (char **) carr->data;
				if (lua_isstring(L, -1)) {
					const char *str = lua_tostring(L, -1);
					strings[i] = strdup(str);
				} else if (lua_isinteger(L, -1)) {
					// Allow raw pointer addresses
					strings[i] = (char *) (uintptr_t) lua_tointeger(L, -1);
				} else {
					strings[i] = NULL;
				}
				break;
			}
		case CARRAY_INT:{
				int *ints = (int *) carr->data;
				ints[i] = (int) luaL_checkinteger(L, -1);
				break;
			}
		case CARRAY_LONG:{
				long *longs = (long *) carr->data;
				longs[i] = (long) luaL_checkinteger(L, -1);
				break;
			}
		case CARRAY_VOIDPTR:{
				void **ptrs = (void **) carr->data;
				ptrs[i] = (void *) (uintptr_t) luaL_checkinteger(L, -1);
				break;
			}
		}
		lua_pop(L, 1);	// Remove value from stack
	}

	// Set metatable
	if (luaL_getmetatable(L, CARRAY_META) == LUA_TNIL) {
		return luaL_error(L, "carray metatable not found - call create_carray_metatable() first");
	}
	lua_setmetatable(L, -2);

	return 1;
}

/**
* Create metatable for carray objects
*/
static void create_carray_metatable(lua_State *L)
{
	static const struct luaL_Reg carray_meta[] = {
		{ "__gc", carray_gc },
		{ "__index", carray_index },
		{ "__newindex", carray_newindex },
		{ "__len", carray_len },
		{ NULL, NULL }
	};

	luaL_newmetatable(L, CARRAY_META);
	luaL_setfuncs(L, carray_meta, 0);
	lua_pop(L, 1);
}

/**
* Initialize lua2c functionality 
*/
void init_lua2c(lua_State *L)
{
	create_carray_metatable(L);
	lua_pushcfunction(L, print_array);
	lua_setglobal(L, "print_array");
}



/**
* struct2c() - JSON-defined C structures for wsh
* Extends lua2c() with complex structure support
*/


/**
* GC for struct definitions
*/
static int struct_def_gc(lua_State *L)
{
	struct_def_t **def_ptr = (struct_def_t **) lua_touserdata(L, 1);
	if (def_ptr && *def_ptr) {
		free((*def_ptr)->fields);
		free(*def_ptr);
		*def_ptr = NULL;
	}
	return 0;
}

/**
* Garbage collection for structures
*/
static int cstruct_gc(lua_State *L)
{
	cstruct_t *cstruct = (cstruct_t *) luaL_checkudata(L, 1, CSTRUCT_META);
	if (cstruct->is_owned && cstruct->data) {
		// Free any allocated strings first
		struct_def_t *def = cstruct->definition;
		for (size_t i = 0; i < def->field_count; i++) {
			struct_field_t *field = &def->fields[i];
			if (strcmp(field->type, "char*") == 0) {
				void *field_ptr = (char *) cstruct->data + field->offset;
				char *str = *(char **) field_ptr;
				// Free string if it looks like we allocated it (not a low address)
				if (str && (uintptr_t) str > 0x10000) {
					free(str);
				}
			}
		}

		// Free the structure itself
		free(cstruct->data);
		cstruct->data = NULL;
	}
	// Note: struct_def_t is owned by separate userdata with its own GC
	return 0;
}

/**
* Get raw C pointer
*/
static int cstruct_ptr(lua_State *L)
{
	cstruct_t *cstruct = (cstruct_t *) luaL_checkudata(L, 1, CSTRUCT_META);
	lua_pushinteger(L, (lua_Integer) (uintptr_t) cstruct->data);
	return 1;
}

/**
* Field access: struct.field_name
*/
static int cstruct_index(lua_State *L)
{
	cstruct_t *cstruct = (cstruct_t *) luaL_checkudata(L, 1, CSTRUCT_META);
	const char *field_name = luaL_checkstring(L, 2);

	// Check for methods first
	if (strcmp(field_name, "ptr") == 0) {
		lua_pushcfunction(L, cstruct_ptr);
		return 1;
	}
	// Look for field by name
	struct_def_t *def = cstruct->definition;
	for (size_t i = 0; i < def->field_count; i++) {
		if (strcmp(def->fields[i].name, field_name) == 0) {
			struct_field_t *field = &def->fields[i];
			void *field_ptr = (char *) cstruct->data + field->offset;

			// Convert C value to Lua based on type
			if (strcmp(field->type, "int") == 0) {
				lua_pushinteger(L, *(int *) field_ptr);
			} else if (strcmp(field->type, "unsigned int") == 0) {
				lua_pushinteger(L, *(unsigned int *) field_ptr);
			} else if (strcmp(field->type, "long") == 0) {
				lua_pushinteger(L, *(long *) field_ptr);
			} else if (strcmp(field->type, "unsigned long") == 0) {
				lua_pushinteger(L, *(unsigned long *) field_ptr);
			} else if (strcmp(field->type, "unsigned short") == 0) {
				lua_pushinteger(L, *(unsigned short *) field_ptr);
			} else if (strcmp(field->type, "unsigned char") == 0) {
				lua_pushinteger(L, *(unsigned char *) field_ptr);
			} else if (strcmp(field->type, "char*") == 0) {
				char *str = *(char **) field_ptr;
				if (str) {
					lua_pushstring(L, str);
				} else {
					lua_pushnil(L);
				}
			} else if (strcmp(field->type, "void*") == 0) {
				lua_pushinteger(L, (lua_Integer) (uintptr_t) (*(void **) field_ptr));
			} else if (strstr(field->type, "[") != NULL) {
				// Handle arrays - return as hex string for now
				char hex_str[256] = { 0 };
				snprintf(hex_str, sizeof(hex_str), "Array@%p[%zu]", field_ptr, field->size);
				lua_pushstring(L, hex_str);
			} else {
				// Unknown type, return as raw pointer
				lua_pushinteger(L, (lua_Integer) (uintptr_t) field_ptr);
			}
			return 1;
		}
	}

	// Field not found
	lua_pushnil(L);
	return 1;
}

/**
* Field assignment: struct.field_name = value
*/
static int cstruct_newindex(lua_State *L)
{
	cstruct_t *cstruct = (cstruct_t *) luaL_checkudata(L, 1, CSTRUCT_META);
	const char *field_name = luaL_checkstring(L, 2);

	// Look for field by name
	struct_def_t *def = cstruct->definition;
	for (size_t i = 0; i < def->field_count; i++) {
		if (strcmp(def->fields[i].name, field_name) == 0) {
			struct_field_t *field = &def->fields[i];
			void *field_ptr = (char *) cstruct->data + field->offset;

			// Set C value from Lua based on type
			if (strcmp(field->type, "int") == 0) {
				*(int *) field_ptr = (int) luaL_checkinteger(L, 3);
			} else if (strcmp(field->type, "unsigned int") == 0) {
				*(unsigned int *) field_ptr = (unsigned int) luaL_checkinteger(L, 3);
			} else if (strcmp(field->type, "long") == 0) {
				*(long *) field_ptr = (long) luaL_checkinteger(L, 3);
			} else if (strcmp(field->type, "char*") == 0) {
				char **str_ptr = (char **) field_ptr;
				// Free existing string if it looks like we allocated it
				if (*str_ptr && (uintptr_t) * str_ptr > 0x10000) {
					free(*str_ptr);
				}
				// Set new string
				if (lua_isstring(L, 3)) {
					const char *str = lua_tostring(L, 3);
					*str_ptr = strdup(str);
				} else if (lua_isinteger(L, 3)) {
					*str_ptr = (char *) (uintptr_t) lua_tointeger(L, 3);
				} else {
					*str_ptr = NULL;
				}
			} else if (strcmp(field->type, "void*") == 0) {
				*(void **) field_ptr = (void *) (uintptr_t) luaL_checkinteger(L, 3);
			}
			return 0;
		}
	}

	return luaL_error(L, "unknown field '%s' in structure", field_name);
}

/**
* Debug function to print structure contents
*/
static int print_struct(lua_State *L)
{
	cstruct_t *cstruct = (cstruct_t *) luaL_testudata(L, 1, CSTRUCT_META);
	if (!cstruct) {
		return luaL_argerror(L, 1, "expected cstruct (created with struct2c)");
	}

	struct_def_t *def = cstruct->definition;
	printf("Struct %s (%zu bytes) at %p:\n", def->name, def->total_size, cstruct->data);

	for (size_t i = 0; i < def->field_count; i++) {
		struct_field_t *field = &def->fields[i];
		void *field_ptr = (char *) cstruct->data + field->offset;

		printf("  .%s (@%zu, %s) = ", field->name, field->offset, field->type);

		if (strcmp(field->type, "int") == 0) {
			printf("%d", *(int *) field_ptr);
		} else if (strcmp(field->type, "unsigned int") == 0) {
			printf("%u", *(unsigned int *) field_ptr);
		} else if (strcmp(field->type, "long") == 0) {
			printf("%ld", *(long *) field_ptr);
		} else if (strcmp(field->type, "unsigned long") == 0) {
			printf("0x%lx", *(unsigned long *) field_ptr);
		} else if (strcmp(field->type, "unsigned short") == 0) {
			printf("%u", *(unsigned short *) field_ptr);
		} else if (strcmp(field->type, "unsigned char") == 0) {
			printf("%u", *(unsigned char *) field_ptr);
		} else if (strcmp(field->type, "char*") == 0) {
			char *str = *(char **) field_ptr;
			if (str) {
				printf("\"%s\" (at %p)", str, (void *) str);
			} else {
				printf("NULL");
			}
		} else if (strcmp(field->type, "void*") == 0) {
			printf("%p", *(void **) field_ptr);
		} else if (strstr(field->type, "[") != NULL) {
			// Handle arrays - show first few bytes as hex
			printf("[ ");
			unsigned char *bytes = (unsigned char *) field_ptr;
			size_t show_bytes = (field->size > 16) ? 16 : field->size;
			for (size_t j = 0; j < show_bytes; j++) {
				printf("%02x ", bytes[j]);
			}
			if (field->size > 16)
				printf("... ");
			printf("]");
		} else {
			printf("<%s at %p>", field->type, field_ptr);
		}
		printf("\n");
	}

	return 0;
}

/**
* Load JSON structure definition: load_struct_def("filename.json")
*/
static int load_struct_def(lua_State *L)
{
	const char *filename = luaL_checkstring(L, 1);

	// Read JSON file
	FILE *f = fopen(filename, "r");
	if (!f) {
		return luaL_error(L, "cannot open file '%s'", filename);
	}
	// Get file size
	fseek(f, 0, SEEK_END);
	long fsize = ftell(f);
	fseek(f, 0, SEEK_SET);

	// Read file contents
	char *json_str = malloc(fsize + 1);
	fread(json_str, fsize, 1, f);
	json_str[fsize] = '\0';
	fclose(f);

	// Parse JSON using Lua's json library
	lua_getglobal(L, "json");
	if (lua_isnil(L, -1)) {
		free(json_str);
		return luaL_error(L, "json library not loaded - require('json') first");
	}

	lua_getfield(L, -1, "decode");
	lua_pushstring(L, json_str);
	lua_call(L, 1, 1);	// Call json.decode(json_str)
	free(json_str);
	lua_remove(L, -2);	// Remove json module from stack

	// Now we have the parsed JSON table on stack
	// Convert to struct_def_t
	struct_def_t *def = malloc(sizeof(struct_def_t));
	memset(def, 0, sizeof(struct_def_t));

	// Extract structure name
	lua_getfield(L, -1, "name");
	if (lua_isstring(L, -1)) {
		strncpy(def->name, lua_tostring(L, -1), sizeof(def->name) - 1);
	}
	lua_pop(L, 1);

	// Extract size
	lua_getfield(L, -1, "size");
	if (lua_isinteger(L, -1)) {
		def->total_size = (size_t) lua_tointeger(L, -1);
	}
	lua_pop(L, 1);

	// Extract alignment (optional, default to 8)
	lua_getfield(L, -1, "alignment");
	if (lua_isinteger(L, -1)) {
		def->alignment = (size_t) lua_tointeger(L, -1);
	} else {
		def->alignment = 8;	// Default alignment
	}
	lua_pop(L, 1);

	// Extract fields array
	lua_getfield(L, -1, "fields");
	if (!lua_istable(L, -1)) {
		free(def);
		return luaL_error(L, "structure definition missing 'fields' array");
	}
	// Get number of fields
	def->field_count = lua_rawlen(L, -1);
	if (def->field_count == 0) {
		free(def);
		lua_pop(L, 2);
		return luaL_error(L, "structure has no fields");
	}
	// Allocate fields array
	def->fields = malloc(def->field_count * sizeof(struct_field_t));

	// Parse each field
	for (size_t i = 0; i < def->field_count; i++) {
		lua_rawgeti(L, -1, i + 1);	// Get field[i]

		if (!lua_istable(L, -1)) {
			free(def->fields);
			free(def);
			return luaL_error(L, "field %zu is not a table", i);
		}

		struct_field_t *field = &def->fields[i];
		memset(field, 0, sizeof(struct_field_t));

		// Field name
		lua_getfield(L, -1, "name");
		if (lua_isstring(L, -1)) {
			strncpy(field->name, lua_tostring(L, -1), sizeof(field->name) - 1);
		}
		lua_pop(L, 1);

		// Field type
		lua_getfield(L, -1, "type");
		if (lua_isstring(L, -1)) {
			strncpy(field->type, lua_tostring(L, -1), sizeof(field->type) - 1);
		}
		lua_pop(L, 1);

		// Field offset
		lua_getfield(L, -1, "offset");
		if (lua_isinteger(L, -1)) {
			field->offset = (size_t) lua_tointeger(L, -1);
		}
		lua_pop(L, 1);

		// Field size
		lua_getfield(L, -1, "size");
		if (lua_isinteger(L, -1)) {
			field->size = (size_t) lua_tointeger(L, -1);
		}
		lua_pop(L, 1);

		lua_pop(L, 1);	// Remove field table
	}

	lua_pop(L, 2);		// Remove fields array and main table

	// Store definition in userdata with proper metatable
	struct_def_t **def_ptr = (struct_def_t **) lua_newuserdata(L, sizeof(struct_def_t *));
	*def_ptr = def;

	// Set the proper metatable
	luaL_getmetatable(L, STRUCT_DEF_META);
	lua_setmetatable(L, -2);

	return 1;		// Return userdata containing struct definition
}

/**
* Create metatable for structure objects
*/
static void create_cstruct_metatable(lua_State *L)
{
	static const struct luaL_Reg cstruct_meta[] = {
		{ "__gc", cstruct_gc },
		{ "__index", cstruct_index },
		{ "__newindex", cstruct_newindex },
		{ NULL, NULL }
	};

	luaL_newmetatable(L, CSTRUCT_META);
	luaL_setfuncs(L, cstruct_meta, 0);
	lua_pop(L, 1);
}

/**
* Create metatable for structure definitions
*/
static void create_struct_def_metatable(lua_State *L)
{
	static const struct luaL_Reg struct_def_meta[] = {
		{ "__gc", struct_def_gc },
		{ NULL, NULL }
	};

	luaL_newmetatable(L, STRUCT_DEF_META);
	luaL_setfuncs(L, struct_def_meta, 0);
	lua_pop(L, 1);
}

/**
* Main structure creation function: struct2c(data, struct_def)
*/
static int struct2c(lua_State *L)
{
	// Arg 1: Lua table with field data
	luaL_checktype(L, 1, LUA_TTABLE);

	// Arg 2: Structure definition (userdata from load_struct_def)
	struct_def_t **def_ptr = (struct_def_t **) luaL_checkudata(L, 2, STRUCT_DEF_META);
	if (!def_ptr || !*def_ptr) {
		return luaL_error(L, "invalid structure definition");
	}

	struct_def_t *def = *def_ptr;

	// Allocate memory for the C structure
	void *struct_data = calloc(1, def->total_size);
	if (!struct_data) {
		return luaL_error(L, "memory allocation failed for structure");
	}
	// Populate fields from Lua table
	for (size_t i = 0; i < def->field_count; i++) {
		struct_field_t *field = &def->fields[i];
		void *field_ptr = (char *) struct_data + field->offset;

		// Skip padding fields (names starting with _pad)
		if (strncmp(field->name, "_pad", 4) == 0) {
			continue;
		}
		// Get field value from Lua table
		lua_getfield(L, 1, field->name);

		if (lua_isnil(L, -1)) {
			// Field not provided, leave as zero (calloc initialized)
			lua_pop(L, 1);
			continue;
		}
		// Set field value based on type
		if (strcmp(field->type, "int") == 0) {
			if (lua_isinteger(L, -1)) {
				*(int *) field_ptr = (int) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "unsigned int") == 0) {
			if (lua_isinteger(L, -1)) {
				*(unsigned int *) field_ptr = (unsigned int) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "long") == 0) {
			if (lua_isinteger(L, -1)) {
				*(long *) field_ptr = (long) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "unsigned long") == 0) {
			if (lua_isinteger(L, -1)) {
				*(unsigned long *) field_ptr = (unsigned long) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "unsigned short") == 0) {
			if (lua_isinteger(L, -1)) {
				*(unsigned short *) field_ptr = (unsigned short) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "unsigned char") == 0) {
			if (lua_isinteger(L, -1)) {
				*(unsigned char *) field_ptr = (unsigned char) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "char*") == 0) {
			if (lua_isstring(L, -1)) {
				const char *str = lua_tostring(L, -1);
				*(char **) field_ptr = strdup(str);
			} else if (lua_isinteger(L, -1)) {
				// Allow raw pointer values
				*(char **) field_ptr = (char *) (uintptr_t) lua_tointeger(L, -1);
			}
		} else if (strcmp(field->type, "void*") == 0) {
			if (lua_isinteger(L, -1)) {
				*(void **) field_ptr = (void *) (uintptr_t) lua_tointeger(L, -1);
			}
		} else if (strstr(field->type, "[") != NULL) {
			// Handle arrays - skip for now in struct2c (would need special handling)
			printf("Warning: array field '%s' of type '%s' skipped in struct2c\n", field->name, field->type);
		} else {
			// Unknown type - skip with warning
			printf("Warning: unknown field type '%s' for field '%s'\n", field->type, field->name);
		}

		lua_pop(L, 1);	// Remove field value
	}

	// Create cstruct userdata
	cstruct_t *cstruct = (cstruct_t *) lua_newuserdata(L, sizeof(cstruct_t));
	cstruct->data = struct_data;
	cstruct->definition = def;
	cstruct->is_owned = 1;	// We own this memory

	// Set metatable
	luaL_getmetatable(L, CSTRUCT_META);
	lua_setmetatable(L, -2);

	return 1;		// Return cstruct userdata
}

/**
* Overlay structure definition onto existing memory: ptr2struct(memory_ptr, size, struct_def)
*/
static int ptr2struct(lua_State *L)
{
	// Arg 1: Memory pointer (integer address)
	lua_Integer ptr_addr = luaL_checkinteger(L, 1);
	void *memory_ptr = (void *) (uintptr_t) ptr_addr;

	// Arg 2: Size of memory region (for bounds checking)
	size_t memory_size = (size_t) luaL_checkinteger(L, 2);

	// Arg 3: Structure definition
	struct_def_t **def_ptr = (struct_def_t **) luaL_checkudata(L, 3, STRUCT_DEF_META);
	if (!def_ptr || !*def_ptr) {
		return luaL_error(L, "invalid structure definition");
	}

	struct_def_t *def = *def_ptr;

	// Validate memory region size
	if (memory_size < def->total_size) {
		return luaL_error(L, "memory region too small: need %zu bytes, got %zu", def->total_size, memory_size);
	}
	// Create cstruct that points to existing memory (not owned)
	cstruct_t *cstruct = (cstruct_t *) lua_newuserdata(L, sizeof(cstruct_t));
	cstruct->data = memory_ptr;
	cstruct->definition = def;
	cstruct->is_owned = 0;	// We don't own this memory - don't free it!

	// Set metatable
	luaL_getmetatable(L, CSTRUCT_META);
	lua_setmetatable(L, -2);

	return 1;		// Return cstruct userdata (overlay view)
}

/**
* Copy memory data into new managed structure: memory2c(memory_ptr, struct_def)
*/
static int memory2c(lua_State *L)
{
	// Arg 1: Memory pointer (integer address)
	lua_Integer ptr_addr = luaL_checkinteger(L, 1);
	void *memory_ptr = (void *) (uintptr_t) ptr_addr;

	// Arg 2: Structure definition
	struct_def_t **def_ptr = (struct_def_t **) luaL_checkudata(L, 2, STRUCT_DEF_META);
	if (!def_ptr || !*def_ptr) {
		return luaL_error(L, "invalid structure definition");
	}

	struct_def_t *def = *def_ptr;

	// Allocate new memory for the structure
	void *struct_data = malloc(def->total_size);
	if (!struct_data) {
		return luaL_error(L, "memory allocation failed for structure");
	}
	// Copy raw memory data
	memcpy(struct_data, memory_ptr, def->total_size);

	// For string fields, we need to copy the pointed-to strings too
	for (size_t i = 0; i < def->field_count; i++) {
		struct_field_t *field = &def->fields[i];
		if (strcmp(field->type, "char*") == 0) {
			void *field_ptr = (char *) struct_data + field->offset;
			char *original_str = *(char **) field_ptr;
			if (original_str && (uintptr_t) original_str > 0x10000) {
				// Copy the string to our own memory
				*(char **) field_ptr = strdup(original_str);
			}
		}
	}

	// Create cstruct that owns the copied memory
	cstruct_t *cstruct = (cstruct_t *) lua_newuserdata(L, sizeof(cstruct_t));
	cstruct->data = struct_data;
	cstruct->definition = def;
	cstruct->is_owned = 1;	// We own this memory

	// Set metatable
	luaL_getmetatable(L, CSTRUCT_META);
	lua_setmetatable(L, -2);

	return 1;		// Return cstruct userdata (independent copy)
}

/**
* Initialize struct2c functionality
*/
void init_struct2c(lua_State *L)
{
	create_cstruct_metatable(L);
	create_struct_def_metatable(L);
}

